sequel 3.21.0 → 3.41.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (363) hide show
  1. data/CHANGELOG +1131 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +106 -65
  4. data/Rakefile +59 -27
  5. data/bin/sequel +50 -26
  6. data/doc/active_record.rdoc +68 -52
  7. data/doc/advanced_associations.rdoc +201 -75
  8. data/doc/association_basics.rdoc +327 -79
  9. data/doc/bin_sequel.rdoc +144 -0
  10. data/doc/cheat_sheet.rdoc +21 -21
  11. data/doc/core_extensions.rdoc +374 -0
  12. data/doc/dataset_basics.rdoc +10 -10
  13. data/doc/dataset_filtering.rdoc +54 -42
  14. data/doc/mass_assignment.rdoc +56 -0
  15. data/doc/migration.rdoc +89 -557
  16. data/doc/model_hooks.rdoc +80 -29
  17. data/doc/object_model.rdoc +576 -0
  18. data/doc/opening_databases.rdoc +121 -53
  19. data/doc/prepared_statements.rdoc +41 -15
  20. data/doc/querying.rdoc +137 -56
  21. data/doc/reflection.rdoc +36 -10
  22. data/doc/release_notes/3.22.0.txt +39 -0
  23. data/doc/release_notes/3.23.0.txt +172 -0
  24. data/doc/release_notes/3.24.0.txt +420 -0
  25. data/doc/release_notes/3.25.0.txt +88 -0
  26. data/doc/release_notes/3.26.0.txt +88 -0
  27. data/doc/release_notes/3.27.0.txt +82 -0
  28. data/doc/release_notes/3.28.0.txt +304 -0
  29. data/doc/release_notes/3.29.0.txt +459 -0
  30. data/doc/release_notes/3.30.0.txt +135 -0
  31. data/doc/release_notes/3.31.0.txt +146 -0
  32. data/doc/release_notes/3.32.0.txt +202 -0
  33. data/doc/release_notes/3.33.0.txt +157 -0
  34. data/doc/release_notes/3.34.0.txt +671 -0
  35. data/doc/release_notes/3.35.0.txt +144 -0
  36. data/doc/release_notes/3.36.0.txt +245 -0
  37. data/doc/release_notes/3.37.0.txt +338 -0
  38. data/doc/release_notes/3.38.0.txt +234 -0
  39. data/doc/release_notes/3.39.0.txt +237 -0
  40. data/doc/release_notes/3.40.0.txt +73 -0
  41. data/doc/release_notes/3.41.0.txt +155 -0
  42. data/doc/schema_modification.rdoc +585 -0
  43. data/doc/sharding.rdoc +99 -8
  44. data/doc/sql.rdoc +154 -112
  45. data/doc/testing.rdoc +169 -0
  46. data/doc/thread_safety.rdoc +17 -0
  47. data/doc/transactions.rdoc +137 -0
  48. data/doc/validations.rdoc +1 -1
  49. data/doc/virtual_rows.rdoc +78 -43
  50. data/lib/sequel/adapters/ado/access.rb +334 -0
  51. data/lib/sequel/adapters/ado/mssql.rb +18 -12
  52. data/lib/sequel/adapters/ado.rb +69 -21
  53. data/lib/sequel/adapters/amalgalite.rb +17 -14
  54. data/lib/sequel/adapters/cubrid.rb +143 -0
  55. data/lib/sequel/adapters/db2.rb +178 -85
  56. data/lib/sequel/adapters/dbi.rb +19 -21
  57. data/lib/sequel/adapters/do/mysql.rb +17 -11
  58. data/lib/sequel/adapters/do/postgres.rb +2 -61
  59. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  60. data/lib/sequel/adapters/do.rb +20 -39
  61. data/lib/sequel/adapters/firebird.rb +33 -210
  62. data/lib/sequel/adapters/ibmdb.rb +452 -0
  63. data/lib/sequel/adapters/informix.rb +6 -29
  64. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  65. data/lib/sequel/adapters/jdbc/cubrid.rb +52 -0
  66. data/lib/sequel/adapters/jdbc/db2.rb +78 -0
  67. data/lib/sequel/adapters/jdbc/derby.rb +325 -0
  68. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  69. data/lib/sequel/adapters/jdbc/h2.rb +68 -31
  70. data/lib/sequel/adapters/jdbc/hsqldb.rb +189 -0
  71. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  72. data/lib/sequel/adapters/jdbc/jtds.rb +40 -0
  73. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  74. data/lib/sequel/adapters/jdbc/mysql.rb +16 -28
  75. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  76. data/lib/sequel/adapters/jdbc/postgresql.rb +77 -52
  77. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  78. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  79. data/lib/sequel/adapters/jdbc/sqlserver.rb +72 -0
  80. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  81. data/lib/sequel/adapters/jdbc.rb +308 -114
  82. data/lib/sequel/adapters/mock.rb +372 -0
  83. data/lib/sequel/adapters/mysql.rb +130 -230
  84. data/lib/sequel/adapters/mysql2.rb +64 -63
  85. data/lib/sequel/adapters/odbc/mssql.rb +17 -9
  86. data/lib/sequel/adapters/odbc.rb +29 -10
  87. data/lib/sequel/adapters/openbase.rb +7 -11
  88. data/lib/sequel/adapters/oracle.rb +364 -57
  89. data/lib/sequel/adapters/postgres.rb +379 -145
  90. data/lib/sequel/adapters/shared/access.rb +236 -11
  91. data/lib/sequel/adapters/shared/cubrid.rb +216 -0
  92. data/lib/sequel/adapters/shared/db2.rb +353 -0
  93. data/lib/sequel/adapters/shared/firebird.rb +231 -0
  94. data/lib/sequel/adapters/shared/informix.rb +53 -0
  95. data/lib/sequel/adapters/shared/mssql.rb +412 -116
  96. data/lib/sequel/adapters/shared/mysql.rb +396 -89
  97. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +166 -0
  98. data/lib/sequel/adapters/shared/oracle.rb +262 -45
  99. data/lib/sequel/adapters/shared/postgres.rb +688 -279
  100. data/lib/sequel/adapters/shared/progress.rb +5 -8
  101. data/lib/sequel/adapters/shared/sqlite.rb +317 -96
  102. data/lib/sequel/adapters/sqlite.rb +109 -46
  103. data/lib/sequel/adapters/swift/mysql.rb +10 -12
  104. data/lib/sequel/adapters/swift/postgres.rb +13 -79
  105. data/lib/sequel/adapters/swift/sqlite.rb +9 -6
  106. data/lib/sequel/adapters/swift.rb +31 -27
  107. data/lib/sequel/adapters/tinytds.rb +177 -25
  108. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +77 -0
  109. data/lib/sequel/adapters/utils/pg_types.rb +81 -0
  110. data/lib/sequel/adapters/utils/split_alter_table.rb +36 -0
  111. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  112. data/lib/sequel/ast_transformer.rb +194 -0
  113. data/lib/sequel/connection_pool/sharded_single.rb +23 -13
  114. data/lib/sequel/connection_pool/sharded_threaded.rb +64 -13
  115. data/lib/sequel/connection_pool/single.rb +11 -3
  116. data/lib/sequel/connection_pool/threaded.rb +69 -20
  117. data/lib/sequel/connection_pool.rb +20 -18
  118. data/lib/sequel/core.rb +143 -11
  119. data/lib/sequel/database/connecting.rb +66 -29
  120. data/lib/sequel/database/dataset.rb +4 -4
  121. data/lib/sequel/database/dataset_defaults.rb +63 -2
  122. data/lib/sequel/database/logging.rb +7 -2
  123. data/lib/sequel/database/misc.rb +192 -51
  124. data/lib/sequel/database/query.rb +299 -114
  125. data/lib/sequel/database/schema_generator.rb +103 -36
  126. data/lib/sequel/database/schema_methods.rb +310 -69
  127. data/lib/sequel/dataset/actions.rb +344 -90
  128. data/lib/sequel/dataset/features.rb +101 -8
  129. data/lib/sequel/dataset/graph.rb +42 -15
  130. data/lib/sequel/dataset/misc.rb +52 -25
  131. data/lib/sequel/dataset/mutation.rb +12 -7
  132. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  133. data/lib/sequel/dataset/query.rb +322 -68
  134. data/lib/sequel/dataset/sql.rb +716 -293
  135. data/lib/sequel/dataset.rb +8 -0
  136. data/lib/sequel/exceptions.rb +4 -0
  137. data/lib/sequel/extensions/_pretty_table.rb +83 -0
  138. data/lib/sequel/extensions/arbitrary_servers.rb +109 -0
  139. data/lib/sequel/extensions/blank.rb +4 -0
  140. data/lib/sequel/extensions/columns_introspection.rb +72 -0
  141. data/lib/sequel/extensions/connection_validator.rb +109 -0
  142. data/lib/sequel/extensions/constraint_validations.rb +451 -0
  143. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +30 -46
  144. data/lib/sequel/extensions/eval_inspect.rb +173 -0
  145. data/lib/sequel/extensions/inflector.rb +4 -0
  146. data/lib/sequel/extensions/looser_typecasting.rb +5 -4
  147. data/lib/sequel/extensions/migration.rb +100 -11
  148. data/lib/sequel/extensions/named_timezones.rb +9 -0
  149. data/lib/sequel/extensions/null_dataset.rb +94 -0
  150. data/lib/sequel/extensions/pagination.rb +4 -0
  151. data/lib/sequel/extensions/pg_array.rb +539 -0
  152. data/lib/sequel/extensions/pg_array_ops.rb +263 -0
  153. data/lib/sequel/extensions/pg_auto_parameterize.rb +175 -0
  154. data/lib/sequel/extensions/pg_hstore.rb +334 -0
  155. data/lib/sequel/extensions/pg_hstore_ops.rb +293 -0
  156. data/lib/sequel/extensions/pg_inet.rb +113 -0
  157. data/lib/sequel/extensions/pg_interval.rb +191 -0
  158. data/lib/sequel/extensions/pg_json.rb +212 -0
  159. data/lib/sequel/extensions/pg_range.rb +513 -0
  160. data/lib/sequel/extensions/pg_range_ops.rb +150 -0
  161. data/lib/sequel/extensions/pg_row.rb +576 -0
  162. data/lib/sequel/extensions/pg_row_ops.rb +182 -0
  163. data/lib/sequel/extensions/pg_statement_cache.rb +317 -0
  164. data/lib/sequel/extensions/pretty_table.rb +17 -72
  165. data/lib/sequel/extensions/query.rb +8 -4
  166. data/lib/sequel/extensions/query_literals.rb +79 -0
  167. data/lib/sequel/extensions/schema_caching.rb +76 -0
  168. data/lib/sequel/extensions/schema_dumper.rb +282 -76
  169. data/lib/sequel/extensions/select_remove.rb +39 -0
  170. data/lib/sequel/extensions/server_block.rb +140 -0
  171. data/lib/sequel/extensions/split_array_nil.rb +65 -0
  172. data/lib/sequel/extensions/sql_expr.rb +8 -110
  173. data/lib/sequel/extensions/string_date_time.rb +4 -0
  174. data/lib/sequel/extensions/thread_local_timezones.rb +10 -4
  175. data/lib/sequel/extensions/to_dot.rb +99 -83
  176. data/lib/sequel/model/associations.rb +1263 -409
  177. data/lib/sequel/model/base.rb +624 -171
  178. data/lib/sequel/model/errors.rb +1 -1
  179. data/lib/sequel/model/exceptions.rb +24 -2
  180. data/lib/sequel/model/inflections.rb +1 -1
  181. data/lib/sequel/model.rb +30 -11
  182. data/lib/sequel/no_core_ext.rb +2 -0
  183. data/lib/sequel/plugins/active_model.rb +13 -1
  184. data/lib/sequel/plugins/association_pks.rb +137 -16
  185. data/lib/sequel/plugins/caching.rb +25 -18
  186. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  187. data/lib/sequel/plugins/composition.rb +44 -12
  188. data/lib/sequel/plugins/constraint_validations.rb +198 -0
  189. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  190. data/lib/sequel/plugins/defaults_setter.rb +72 -0
  191. data/lib/sequel/plugins/dirty.rb +214 -0
  192. data/lib/sequel/plugins/eager_each.rb +59 -0
  193. data/lib/sequel/plugins/force_encoding.rb +6 -6
  194. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  195. data/lib/sequel/plugins/identity_map.rb +134 -15
  196. data/lib/sequel/plugins/instance_filters.rb +10 -0
  197. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  198. data/lib/sequel/plugins/json_serializer.rb +77 -6
  199. data/lib/sequel/plugins/list.rb +13 -2
  200. data/lib/sequel/plugins/many_through_many.rb +103 -51
  201. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  202. data/lib/sequel/plugins/nested_attributes.rb +150 -66
  203. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  204. data/lib/sequel/plugins/pg_row.rb +121 -0
  205. data/lib/sequel/plugins/pg_typecast_on_load.rb +65 -0
  206. data/lib/sequel/plugins/prepared_statements.rb +168 -0
  207. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  208. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  209. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  210. data/lib/sequel/plugins/rcte_tree.rb +31 -17
  211. data/lib/sequel/plugins/schema.rb +8 -3
  212. data/lib/sequel/plugins/serialization.rb +98 -49
  213. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  214. data/lib/sequel/plugins/sharding.rb +21 -54
  215. data/lib/sequel/plugins/single_table_inheritance.rb +57 -12
  216. data/lib/sequel/plugins/static_cache.rb +99 -0
  217. data/lib/sequel/plugins/subclasses.rb +29 -3
  218. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  219. data/lib/sequel/plugins/timestamps.rb +1 -1
  220. data/lib/sequel/plugins/touch.rb +18 -6
  221. data/lib/sequel/plugins/tree.rb +3 -3
  222. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  223. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  224. data/lib/sequel/plugins/validation_class_methods.rb +2 -1
  225. data/lib/sequel/plugins/validation_helpers.rb +57 -4
  226. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  227. data/lib/sequel/sql.rb +721 -141
  228. data/lib/sequel/timezones.rb +67 -40
  229. data/lib/sequel/version.rb +1 -1
  230. data/spec/adapters/db2_spec.rb +146 -0
  231. data/spec/adapters/firebird_spec.rb +53 -39
  232. data/spec/adapters/mssql_spec.rb +261 -66
  233. data/spec/adapters/mysql_spec.rb +597 -352
  234. data/spec/adapters/oracle_spec.rb +92 -117
  235. data/spec/adapters/postgres_spec.rb +2103 -341
  236. data/spec/adapters/spec_helper.rb +16 -6
  237. data/spec/adapters/sqlite_spec.rb +237 -76
  238. data/spec/core/connection_pool_spec.rb +270 -141
  239. data/spec/core/database_spec.rb +980 -524
  240. data/spec/core/dataset_spec.rb +1658 -1243
  241. data/spec/core/expression_filters_spec.rb +741 -334
  242. data/spec/core/mock_adapter_spec.rb +453 -0
  243. data/spec/core/object_graph_spec.rb +88 -129
  244. data/spec/core/schema_generator_spec.rb +4 -4
  245. data/spec/core/schema_spec.rb +556 -53
  246. data/spec/core/spec_helper.rb +7 -48
  247. data/spec/core_extensions_spec.rb +626 -0
  248. data/spec/extensions/active_model_spec.rb +13 -0
  249. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  250. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  251. data/spec/extensions/association_dependencies_spec.rb +15 -25
  252. data/spec/extensions/association_pks_spec.rb +227 -33
  253. data/spec/extensions/association_proxies_spec.rb +4 -4
  254. data/spec/extensions/boolean_readers_spec.rb +25 -25
  255. data/spec/extensions/caching_spec.rb +47 -51
  256. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  257. data/spec/extensions/columns_introspection_spec.rb +91 -0
  258. data/spec/extensions/composition_spec.rb +18 -13
  259. data/spec/extensions/connection_validator_spec.rb +118 -0
  260. data/spec/extensions/constraint_validations_plugin_spec.rb +196 -0
  261. data/spec/extensions/constraint_validations_spec.rb +316 -0
  262. data/spec/extensions/dataset_associations_spec.rb +199 -0
  263. data/spec/extensions/defaults_setter_spec.rb +88 -0
  264. data/spec/extensions/dirty_spec.rb +155 -0
  265. data/spec/extensions/eager_each_spec.rb +34 -0
  266. data/spec/extensions/eval_inspect_spec.rb +67 -0
  267. data/spec/extensions/force_encoding_spec.rb +4 -2
  268. data/spec/extensions/hook_class_methods_spec.rb +97 -128
  269. data/spec/extensions/identity_map_spec.rb +142 -24
  270. data/spec/extensions/inflector_spec.rb +0 -4
  271. data/spec/extensions/instance_filters_spec.rb +11 -21
  272. data/spec/extensions/instance_hooks_spec.rb +72 -0
  273. data/spec/extensions/json_serializer_spec.rb +49 -12
  274. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  275. data/spec/extensions/list_spec.rb +49 -40
  276. data/spec/extensions/looser_typecasting_spec.rb +7 -7
  277. data/spec/extensions/many_through_many_spec.rb +368 -254
  278. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  279. data/spec/extensions/migration_spec.rb +140 -35
  280. data/spec/extensions/named_timezones_spec.rb +29 -11
  281. data/spec/extensions/nested_attributes_spec.rb +268 -89
  282. data/spec/extensions/null_dataset_spec.rb +85 -0
  283. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  284. data/spec/extensions/pg_array_ops_spec.rb +112 -0
  285. data/spec/extensions/pg_array_spec.rb +324 -0
  286. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  287. data/spec/extensions/pg_hstore_ops_spec.rb +140 -0
  288. data/spec/extensions/pg_hstore_spec.rb +200 -0
  289. data/spec/extensions/pg_inet_spec.rb +47 -0
  290. data/spec/extensions/pg_interval_spec.rb +72 -0
  291. data/spec/extensions/pg_json_spec.rb +99 -0
  292. data/spec/extensions/pg_range_ops_spec.rb +56 -0
  293. data/spec/extensions/pg_range_spec.rb +395 -0
  294. data/spec/extensions/pg_row_ops_spec.rb +58 -0
  295. data/spec/extensions/pg_row_plugin_spec.rb +49 -0
  296. data/spec/extensions/pg_row_spec.rb +340 -0
  297. data/spec/extensions/pg_statement_cache_spec.rb +208 -0
  298. data/spec/extensions/pg_typecast_on_load_spec.rb +58 -0
  299. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  300. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  301. data/spec/extensions/prepared_statements_spec.rb +87 -0
  302. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  303. data/spec/extensions/pretty_table_spec.rb +7 -6
  304. data/spec/extensions/query_literals_spec.rb +167 -0
  305. data/spec/extensions/query_spec.rb +9 -3
  306. data/spec/extensions/rcte_tree_spec.rb +50 -43
  307. data/spec/extensions/schema_caching_spec.rb +41 -0
  308. data/spec/extensions/schema_dumper_spec.rb +434 -49
  309. data/spec/extensions/schema_spec.rb +30 -49
  310. data/spec/extensions/select_remove_spec.rb +38 -0
  311. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  312. data/spec/extensions/serialization_spec.rb +86 -48
  313. data/spec/extensions/server_block_spec.rb +90 -0
  314. data/spec/extensions/sharding_spec.rb +69 -143
  315. data/spec/extensions/single_table_inheritance_spec.rb +65 -29
  316. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  317. data/spec/extensions/spec_helper.rb +34 -67
  318. data/spec/extensions/split_array_nil_spec.rb +24 -0
  319. data/spec/extensions/sql_expr_spec.rb +29 -60
  320. data/spec/extensions/static_cache_spec.rb +145 -0
  321. data/spec/extensions/subclasses_spec.rb +14 -0
  322. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  323. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  324. data/spec/extensions/timestamps_spec.rb +6 -6
  325. data/spec/extensions/to_dot_spec.rb +8 -11
  326. data/spec/extensions/touch_spec.rb +53 -14
  327. data/spec/extensions/tree_spec.rb +11 -26
  328. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  329. data/spec/extensions/update_primary_key_spec.rb +30 -24
  330. data/spec/extensions/validation_class_methods_spec.rb +77 -68
  331. data/spec/extensions/validation_helpers_spec.rb +48 -37
  332. data/spec/extensions/xml_serializer_spec.rb +29 -4
  333. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  334. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  335. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  336. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  337. data/spec/integration/associations_test.rb +659 -21
  338. data/spec/integration/database_test.rb +25 -5
  339. data/spec/integration/dataset_test.rb +729 -141
  340. data/spec/integration/eager_loader_test.rb +38 -54
  341. data/spec/integration/migrator_test.rb +2 -3
  342. data/spec/integration/model_test.rb +81 -2
  343. data/spec/integration/plugin_test.rb +871 -160
  344. data/spec/integration/prepared_statement_test.rb +272 -128
  345. data/spec/integration/schema_test.rb +377 -57
  346. data/spec/integration/spec_helper.rb +49 -32
  347. data/spec/integration/timezone_test.rb +38 -12
  348. data/spec/integration/transaction_test.rb +183 -8
  349. data/spec/integration/type_test.rb +40 -8
  350. data/spec/model/association_reflection_spec.rb +207 -8
  351. data/spec/model/associations_spec.rb +1206 -693
  352. data/spec/model/base_spec.rb +334 -100
  353. data/spec/model/dataset_methods_spec.rb +45 -27
  354. data/spec/model/eager_loading_spec.rb +828 -692
  355. data/spec/model/hooks_spec.rb +355 -67
  356. data/spec/model/model_spec.rb +251 -179
  357. data/spec/model/plugins_spec.rb +24 -13
  358. data/spec/model/record_spec.rb +506 -232
  359. data/spec/model/spec_helper.rb +16 -77
  360. data/spec/model/validations_spec.rb +38 -2
  361. data/spec/rcov.opts +2 -0
  362. metadata +223 -81
  363. data/spec/core/core_sql_spec.rb +0 -451
@@ -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,45 @@ 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'
1590
+ end
1591
+
1592
+ specify "should accept a block" do
1593
+ @dataset.reverse{name}.sql.should == 'SELECT * FROM test ORDER BY name DESC'
1594
+ @dataset.reverse_order{name}.sql.should == 'SELECT * FROM test ORDER BY name DESC'
1595
+ @dataset.reverse(:foo){name}.sql.should == 'SELECT * FROM test ORDER BY foo DESC, name DESC'
1596
+ @dataset.reverse_order(:foo){name}.sql.should == 'SELECT * FROM test ORDER BY foo DESC, name DESC'
1597
+ @dataset.reverse(Sequel.desc(:foo)){name}.sql.should == 'SELECT * FROM test ORDER BY foo ASC, name DESC'
1598
+ @dataset.reverse_order(Sequel.desc(:foo)){name}.sql.should == 'SELECT * FROM test ORDER BY foo ASC, name DESC'
1480
1599
  end
1481
1600
  end
1482
1601
 
@@ -1486,42 +1605,39 @@ describe "Dataset#limit" do
1486
1605
  end
1487
1606
 
1488
1607
  specify "should include a LIMIT clause in the select statement" do
1489
- @dataset.limit(10).sql.should ==
1490
- 'SELECT * FROM test LIMIT 10'
1608
+ @dataset.limit(10).sql.should == 'SELECT * FROM test LIMIT 10'
1491
1609
  end
1492
1610
 
1493
1611
  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'
1612
+ @dataset.limit(3..7).sql.should == 'SELECT * FROM test LIMIT 5 OFFSET 3'
1613
+ @dataset.limit(3...7).sql.should == 'SELECT * FROM test LIMIT 4 OFFSET 3'
1499
1614
  end
1500
1615
 
1501
1616
  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
1617
+ @dataset.limit(6, 10).sql.should == 'SELECT * FROM test LIMIT 6 OFFSET 10'
1618
+ end
1505
1619
 
1506
1620
  specify "should convert regular strings to integers" do
1507
- @dataset.limit('6', 'a() - 1').sql.should ==
1508
- 'SELECT * FROM test LIMIT 6 OFFSET 0'
1621
+ @dataset.limit('6', 'a() - 1').sql.should == 'SELECT * FROM test LIMIT 6 OFFSET 0'
1509
1622
  end
1510
1623
 
1511
1624
  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'
1625
+ @dataset.limit(Sequel.lit('6'), Sequel.lit('a() - 1')).sql.should == 'SELECT * FROM test LIMIT 6 OFFSET a() - 1'
1514
1626
  end
1515
1627
 
1516
1628
  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)'
1629
+ @dataset.limit(6, Sequel.function(:a) - 1).sql.should == 'SELECT * FROM test LIMIT 6 OFFSET (a() - 1)'
1630
+ end
1631
+
1632
+ specify "should be able to reset limit and offset with nil values" do
1633
+ @dataset.limit(6).limit(nil).sql.should == 'SELECT * FROM test'
1634
+ @dataset.limit(6, 1).limit(nil).sql.should == 'SELECT * FROM test OFFSET 1'
1635
+ @dataset.limit(6, 1).limit(nil, nil).sql.should == 'SELECT * FROM test'
1519
1636
  end
1520
1637
 
1521
1638
  specify "should work with fixed sql datasets" do
1522
1639
  @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'
1640
+ @dataset.limit(6, 10).sql.should == 'SELECT * FROM (select * from cccc) AS t1 LIMIT 6 OFFSET 10'
1525
1641
  end
1526
1642
 
1527
1643
  specify "should raise an error if an invalid limit or offset is used" do
@@ -1535,15 +1651,20 @@ describe "Dataset#limit" do
1535
1651
  end
1536
1652
 
1537
1653
  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}
1654
+ specify "should returned clone dataset without row_proc" do
1655
+ d = Sequel::Dataset.new(nil)
1656
+ d.row_proc = Proc.new{|r| r}
1657
+ d.naked.row_proc.should be_nil
1658
+ d.row_proc.should_not be_nil
1542
1659
  end
1543
-
1660
+ end
1661
+
1662
+ describe "Dataset#naked!" do
1544
1663
  specify "should remove any existing row_proc" do
1545
- naked = @d2.naked
1546
- naked.row_proc.should be_nil
1664
+ d = Sequel::Dataset.new(nil)
1665
+ d.row_proc = Proc.new{|r| r}
1666
+ d.naked!.row_proc.should be_nil
1667
+ d.row_proc.should be_nil
1547
1668
  end
1548
1669
  end
1549
1670
 
@@ -1555,7 +1676,7 @@ describe "Dataset#qualified_column_name" do
1555
1676
  specify "should return the literal value if not given a symbol" do
1556
1677
  @dataset.literal(@dataset.send(:qualified_column_name, 'ccc__b', :items)).should == "'ccc__b'"
1557
1678
  @dataset.literal(@dataset.send(:qualified_column_name, 3, :items)).should == '3'
1558
- @dataset.literal(@dataset.send(:qualified_column_name, 'a'.lit, :items)).should == 'a'
1679
+ @dataset.literal(@dataset.send(:qualified_column_name, Sequel.lit('a'), :items)).should == 'a'
1559
1680
  end
1560
1681
 
1561
1682
  specify "should qualify the column with the supplied table name if given an unqualified symbol" do
@@ -1567,42 +1688,46 @@ describe "Dataset#qualified_column_name" do
1567
1688
  end
1568
1689
 
1569
1690
  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)
1691
+ @dataset.literal(@dataset.send(:qualified_column_name, :ccc, Sequel.expr(:items).as(:i))).should == 'i.ccc'
1582
1692
  end
1583
1693
  end
1584
1694
 
1585
1695
  describe "Dataset#map" do
1586
1696
  before do
1587
- @d = DummyDataset.new(nil).from(:items)
1697
+ @d = Sequel.mock(:fetch=>[{:a => 1, :b => 2}, {:a => 3, :b => 4}, {:a => 5, :b => 6}])[:items]
1588
1698
  end
1589
1699
 
1590
1700
  specify "should provide the usual functionality if no argument is given" do
1591
- @d.map {|n| n[:a] + n[:b]}.should == [3, 7, 11]
1701
+ @d.map{|n| n[:a] + n[:b]}.should == [3, 7, 11]
1592
1702
  end
1593
1703
 
1594
1704
  specify "should map using #[column name] if column name is given" do
1595
1705
  @d.map(:a).should == [1, 3, 5]
1596
1706
  end
1597
1707
 
1708
+ specify "should support multiple column names if an array of column names is given" do
1709
+ @d.map([:a, :b]).should == [[1, 2], [3, 4], [5, 6]]
1710
+ end
1711
+
1712
+ specify "should not call the row_proc if an argument is given" do
1713
+ @d.row_proc = proc{|r| h = {}; r.keys.each{|k| h[k] = r[k] * 2}; h}
1714
+ @d.map(:a).should == [1, 3, 5]
1715
+ @d.map([:a, :b]).should == [[1, 2], [3, 4], [5, 6]]
1716
+ end
1717
+
1718
+ specify "should call the row_proc if no argument is given" do
1719
+ @d.row_proc = proc{|r| h = {}; r.keys.each{|k| h[k] = r[k] * 2}; h}
1720
+ @d.map{|n| n[:a] + n[:b]}.should == [6, 14, 22]
1721
+ end
1722
+
1598
1723
  specify "should return the complete dataset values if nothing is given" do
1599
- @d.map.to_a.should == DummyDataset::VALUES
1724
+ @d.map.to_a.should == [{:a => 1, :b => 2}, {:a => 3, :b => 4}, {:a => 5, :b => 6}]
1600
1725
  end
1601
1726
  end
1602
1727
 
1603
1728
  describe "Dataset#to_hash" do
1604
1729
  before do
1605
- @d = DummyDataset.new(nil).from(:items)
1730
+ @d = Sequel.mock(:fetch=>[{:a => 1, :b => 2}, {:a => 3, :b => 4}, {:a => 5, :b => 6}])[:items]
1606
1731
  end
1607
1732
 
1608
1733
  specify "should provide a hash with the first column as key and the second as value" do
@@ -1614,11 +1739,73 @@ describe "Dataset#to_hash" do
1614
1739
  @d.to_hash(:a).should == {1 => {:a => 1, :b => 2}, 3 => {:a => 3, :b => 4}, 5 => {:a => 5, :b => 6}}
1615
1740
  @d.to_hash(:b).should == {2 => {:a => 1, :b => 2}, 4 => {:a => 3, :b => 4}, 6 => {:a => 5, :b => 6}}
1616
1741
  end
1742
+
1743
+ specify "should support using an array of columns as either the key or the value" do
1744
+ @d.to_hash([:a, :b], :b).should == {[1, 2] => 2, [3, 4] => 4, [5, 6] => 6}
1745
+ @d.to_hash(:b, [:a, :b]).should == {2 => [1, 2], 4 => [3, 4], 6 => [5, 6]}
1746
+ @d.to_hash([:b, :a], [:a, :b]).should == {[2, 1] => [1, 2], [4, 3] => [3, 4], [6, 5] => [5, 6]}
1747
+ @d.to_hash([:a, :b]).should == {[1, 2] => {:a => 1, :b => 2}, [3, 4] => {:a => 3, :b => 4}, [5, 6] => {:a => 5, :b => 6}}
1748
+ end
1749
+
1750
+ specify "should not call the row_proc if two arguments are given" do
1751
+ @d.row_proc = proc{|r| h = {}; r.keys.each{|k| h[k] = r[k] * 2}; h}
1752
+ @d.to_hash(:a, :b).should == {1 => 2, 3 => 4, 5 => 6}
1753
+ @d.to_hash(:b, :a).should == {2 => 1, 4 => 3, 6 => 5}
1754
+ @d.to_hash([:a, :b], :b).should == {[1, 2] => 2, [3, 4] => 4, [5, 6] => 6}
1755
+ @d.to_hash(:b, [:a, :b]).should == {2 => [1, 2], 4 => [3, 4], 6 => [5, 6]}
1756
+ @d.to_hash([:b, :a], [:a, :b]).should == {[2, 1] => [1, 2], [4, 3] => [3, 4], [6, 5] => [5, 6]}
1757
+ end
1758
+
1759
+ specify "should call the row_proc if only a single argument is given" do
1760
+ @d.row_proc = proc{|r| h = {}; r.keys.each{|k| h[k] = r[k] * 2}; h}
1761
+ @d.to_hash(:a).should == {2 => {:a => 2, :b => 4}, 6 => {:a => 6, :b => 8}, 10 => {:a => 10, :b => 12}}
1762
+ @d.to_hash(:b).should == {4 => {:a => 2, :b => 4}, 8 => {:a => 6, :b => 8}, 12 => {:a => 10, :b => 12}}
1763
+ @d.to_hash([:a, :b]).should == {[2, 4] => {:a => 2, :b => 4}, [6, 8] => {:a => 6, :b => 8}, [10, 12] => {:a => 10, :b => 12}}
1764
+ end
1765
+ end
1766
+
1767
+ describe "Dataset#to_hash_groups" do
1768
+ before do
1769
+ @d = Sequel.mock(:fetch=>[{:a => 1, :b => 2}, {:a => 3, :b => 4}, {:a => 1, :b => 6}, {:a => 7, :b => 4}])[:items]
1770
+ end
1771
+
1772
+ specify "should provide a hash with the first column as key and the second as value" do
1773
+ @d.to_hash_groups(:a, :b).should == {1 => [2, 6], 3 => [4], 7 => [4]}
1774
+ @d.to_hash_groups(:b, :a).should == {2 => [1], 4=>[3, 7], 6=>[1]}
1775
+ end
1776
+
1777
+ 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
1778
+ @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}]}
1779
+ @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}]}
1780
+ end
1781
+
1782
+ specify "should support using an array of columns as either the key or the value" do
1783
+ @d.to_hash_groups([:a, :b], :b).should == {[1, 2] => [2], [3, 4] => [4], [1, 6] => [6], [7, 4]=>[4]}
1784
+ @d.to_hash_groups(:b, [:a, :b]).should == {2 => [[1, 2]], 4 => [[3, 4], [7, 4]], 6 => [[1, 6]]}
1785
+ @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]]}
1786
+ @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}]}
1787
+ end
1788
+
1789
+ specify "should not call the row_proc if two arguments are given" do
1790
+ @d.row_proc = proc{|r| h = {}; r.keys.each{|k| h[k] = r[k] * 2}; h}
1791
+ @d.to_hash_groups(:a, :b).should == {1 => [2, 6], 3 => [4], 7 => [4]}
1792
+ @d.to_hash_groups(:b, :a).should == {2 => [1], 4=>[3, 7], 6=>[1]}
1793
+ @d.to_hash_groups([:a, :b], :b).should == {[1, 2] => [2], [3, 4] => [4], [1, 6] => [6], [7, 4]=>[4]}
1794
+ @d.to_hash_groups(:b, [:a, :b]).should == {2 => [[1, 2]], 4 => [[3, 4], [7, 4]], 6 => [[1, 6]]}
1795
+ @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]]}
1796
+ end
1797
+
1798
+ specify "should call the row_proc if only a single argument is given" do
1799
+ @d.row_proc = proc{|r| h = {}; r.keys.each{|k| h[k] = r[k] * 2}; h}
1800
+ @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}]}
1801
+ @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}]}
1802
+ @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}]}
1803
+ end
1617
1804
  end
1618
1805
 
1619
1806
  describe "Dataset#distinct" do
1620
1807
  before do
1621
- @db = MockDatabase.new
1808
+ @db = Sequel.mock
1622
1809
  @dataset = @db[:test].select(:name)
1623
1810
  end
1624
1811
 
@@ -1634,7 +1821,7 @@ describe "Dataset#distinct" do
1634
1821
  specify "should use DISTINCT ON if columns are given and DISTINCT ON is supported" do
1635
1822
  @dataset.meta_def(:supports_distinct_on?){true}
1636
1823
  @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'
1824
+ @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
1825
  end
1639
1826
 
1640
1827
  specify "should do a subselect for count" do
@@ -1645,132 +1832,119 @@ end
1645
1832
 
1646
1833
  describe "Dataset#count" do
1647
1834
  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)
1835
+ @db = Sequel.mock(:fetch=>{:count=>1})
1836
+ @dataset = @db.from(:test).columns(:count)
1660
1837
  end
1661
1838
 
1662
1839
  specify "should format SQL properly" do
1663
1840
  @dataset.count.should == 1
1664
- @c.sql.should == 'SELECT COUNT(*) AS count FROM test LIMIT 1'
1841
+ @db.sqls.should == ['SELECT COUNT(*) AS count FROM test LIMIT 1']
1842
+ end
1843
+
1844
+ specify "should accept an argument" do
1845
+ @dataset.count(:foo).should == 1
1846
+ @db.sqls.should == ['SELECT COUNT(foo) AS count FROM test LIMIT 1']
1847
+ end
1848
+
1849
+ specify "should work with a nil argument" do
1850
+ @dataset.count(nil).should == 1
1851
+ @db.sqls.should == ['SELECT COUNT(NULL) AS count FROM test LIMIT 1']
1852
+ end
1853
+
1854
+ specify "should accept a virtual row block" do
1855
+ @dataset.count{foo(bar)}.should == 1
1856
+ @db.sqls.should == ['SELECT COUNT(foo(bar)) AS count FROM test LIMIT 1']
1857
+ end
1858
+
1859
+ specify "should raise an Error if given an argument and a block" do
1860
+ proc{@dataset.count(:foo){foo(bar)}}.should raise_error(Sequel::Error)
1665
1861
  end
1666
1862
 
1667
1863
  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'
1864
+ @dataset.filter(Sequel.expr(:abc) < 30).count.should == 1
1865
+ @db.sqls.should == ['SELECT COUNT(*) AS count FROM test WHERE (abc < 30) LIMIT 1']
1670
1866
  end
1671
1867
 
1672
1868
  specify "should count properly for datasets with fixed sql" do
1673
1869
  @dataset.opts[:sql] = "select abc from xyz"
1674
1870
  @dataset.count.should == 1
1675
- @c.sql.should == "SELECT COUNT(*) AS count FROM (select abc from xyz) AS t1 LIMIT 1"
1871
+ @db.sqls.should == ["SELECT COUNT(*) AS count FROM (select abc from xyz) AS t1 LIMIT 1"]
1676
1872
  end
1677
1873
 
1678
1874
  specify "should count properly when using UNION, INTERSECT, or EXCEPT" do
1679
1875
  @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"
1876
+ @db.sqls.should == ["SELECT COUNT(*) AS count FROM (SELECT * FROM test UNION SELECT * FROM test) AS t1 LIMIT 1"]
1681
1877
  @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"
1878
+ @db.sqls.should == ["SELECT COUNT(*) AS count FROM (SELECT * FROM test INTERSECT SELECT * FROM test) AS t1 LIMIT 1"]
1683
1879
  @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"
1880
+ @db.sqls.should == ["SELECT COUNT(*) AS count FROM (SELECT * FROM test EXCEPT SELECT * FROM test) AS t1 LIMIT 1"]
1685
1881
  end
1686
1882
 
1687
1883
  specify "should return limit if count is greater than it" do
1688
1884
  @dataset.limit(5).count.should == 1
1689
- @c.sql.should == "SELECT COUNT(*) AS count FROM (SELECT * FROM test LIMIT 5) AS t1 LIMIT 1"
1885
+ @db.sqls.should == ["SELECT COUNT(*) AS count FROM (SELECT * FROM test LIMIT 5) AS t1 LIMIT 1"]
1690
1886
  end
1691
1887
 
1692
1888
  it "should work on a graphed_dataset" do
1693
1889
  @dataset.should_receive(:columns).twice.and_return([:a])
1694
1890
  @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'
1891
+ @db.sqls.should == ['SELECT COUNT(*) AS count FROM test LEFT OUTER JOIN test AS test2 USING (a) LIMIT 1']
1696
1892
  end
1697
1893
 
1698
1894
  specify "should not cache the columns value" do
1699
- ds = @dataset.from(:blah)
1895
+ ds = @dataset.from(:blah).columns(:a)
1700
1896
  ds.columns.should == [:a]
1701
1897
  ds.count.should == 1
1702
- @c.sql.should == 'SELECT COUNT(*) AS count FROM blah LIMIT 1'
1898
+ @db.sqls.should == ['SELECT COUNT(*) AS count FROM blah LIMIT 1']
1703
1899
  ds.columns.should == [:a]
1704
1900
  end
1705
1901
  end
1706
1902
 
1707
-
1708
1903
  describe "Dataset#group_and_count" do
1709
1904
  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)
1905
+ @ds = Sequel::Dataset.new(nil).from(:test)
1721
1906
  end
1722
1907
 
1723
1908
  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"
1909
+ @ds.group_and_count(:name).sql.should == "SELECT name, count(*) AS count FROM test GROUP BY name"
1726
1910
  end
1727
1911
 
1728
1912
  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"
1913
+ @ds.group_and_count(:a, :b).sql.should == "SELECT a, b, count(*) AS count FROM test GROUP BY a, b"
1731
1914
  end
1732
1915
 
1733
1916
  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"
1917
+ @ds.group_and_count(:name___n).sql.should == "SELECT name AS n, count(*) AS count FROM test GROUP BY name"
1918
+ @ds.group_and_count(:name__n).sql.should == "SELECT name.n, count(*) AS count FROM test GROUP BY name.n"
1738
1919
  end
1739
1920
 
1740
1921
  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"
1922
+ @ds.group_and_count(Sequel.identifier(:name___n)).sql.should == "SELECT name___n, count(*) AS count FROM test GROUP BY name___n"
1743
1923
  end
1744
1924
 
1745
1925
  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"
1926
+ @ds.group_and_count(Sequel.lit("name")).sql.should == "SELECT name, count(*) AS count FROM test GROUP BY name"
1748
1927
  end
1749
1928
 
1750
1929
  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"
1930
+ @ds.group_and_count(Sequel.expr(:name).as(:n)).sql.should == "SELECT name AS n, count(*) AS count FROM test GROUP BY name"
1931
+ @ds.group_and_count(Sequel.identifier(:name).as(:n)).sql.should == "SELECT name AS n, count(*) AS count FROM test GROUP BY name"
1932
+ end
1933
+
1934
+ specify "should take a virtual row block" do
1935
+ @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)"
1936
+ @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)"
1937
+ @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
1938
  end
1756
1939
  end
1757
1940
 
1758
1941
  describe "Dataset#empty?" do
1759
1942
  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"
1943
+ db = Sequel.mock(:fetch=>proc{|sql| {1=>1} unless sql =~ /WHERE 'f'/})
1944
+ db.from(:test).should_not be_empty
1945
+ db.sqls.should == ['SELECT 1 AS one FROM test LIMIT 1']
1946
+ db.from(:test).filter(false).should be_empty
1947
+ db.sqls.should == ["SELECT 1 AS one FROM test WHERE 'f' LIMIT 1"]
1774
1948
  end
1775
1949
  end
1776
1950
 
@@ -1781,22 +1955,22 @@ describe "Dataset#first_source_alias" do
1781
1955
 
1782
1956
  specify "should be the entire first source if not aliased" do
1783
1957
  @ds.from(:t).first_source_alias.should == :t
1784
- @ds.from(:t__a.identifier).first_source_alias.should == :t__a.identifier
1958
+ @ds.from(Sequel.identifier(:t__a)).first_source_alias.should == Sequel.identifier(:t__a)
1785
1959
  @ds.from(:s__t).first_source_alias.should == :s__t
1786
- @ds.from(:t.qualify(:s)).first_source_alias.should == :t.qualify(:s)
1960
+ @ds.from(Sequel.qualify(:s, :t)).first_source_alias.should == Sequel.qualify(:s, :t)
1787
1961
  end
1788
1962
 
1789
1963
  specify "should be the alias if aliased" do
1790
1964
  @ds.from(:t___a).first_source_alias.should == :a
1791
1965
  @ds.from(:s__t___a).first_source_alias.should == :a
1792
- @ds.from(:t.as(:a)).first_source_alias.should == :a
1966
+ @ds.from(Sequel.expr(:t).as(:a)).first_source_alias.should == :a
1793
1967
  end
1794
1968
 
1795
1969
  specify "should be aliased as first_source" do
1796
1970
  @ds.from(:t).first_source.should == :t
1797
- @ds.from(:t__a.identifier).first_source.should == :t__a.identifier
1971
+ @ds.from(Sequel.identifier(:t__a)).first_source.should == Sequel.identifier(:t__a)
1798
1972
  @ds.from(:s__t___a).first_source.should == :a
1799
- @ds.from(:t.as(:a)).first_source.should == :a
1973
+ @ds.from(Sequel.expr(:t).as(:a)).first_source.should == :a
1800
1974
  end
1801
1975
 
1802
1976
  specify "should raise exception if table doesn't have a source" do
@@ -1811,15 +1985,15 @@ describe "Dataset#first_source_table" do
1811
1985
 
1812
1986
  specify "should be the entire first source if not aliased" do
1813
1987
  @ds.from(:t).first_source_table.should == :t
1814
- @ds.from(:t__a.identifier).first_source_table.should == :t__a.identifier
1988
+ @ds.from(Sequel.identifier(:t__a)).first_source_table.should == Sequel.identifier(:t__a)
1815
1989
  @ds.from(:s__t).first_source_table.should == :s__t
1816
- @ds.from(:t.qualify(:s)).first_source_table.should == :t.qualify(:s)
1990
+ @ds.from(Sequel.qualify(:s, :t)).first_source_table.should == Sequel.qualify(:s, :t)
1817
1991
  end
1818
1992
 
1819
1993
  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
1994
+ @ds.literal(@ds.from(:t___a).first_source_table).should == "t"
1995
+ @ds.literal(@ds.from(:s__t___a).first_source_table).should == "s.t"
1996
+ @ds.literal(@ds.from(Sequel.expr(:t).as(:a)).first_source_table).should == "t"
1823
1997
  end
1824
1998
 
1825
1999
  specify "should raise exception if table doesn't have a source" do
@@ -1849,73 +2023,64 @@ describe "Dataset#from_self" do
1849
2023
  'SELECT * FROM (SELECT name FROM test LIMIT 1) AS some_name INNER JOIN posts ON (posts.alias = some_name.name)'
1850
2024
  end
1851
2025
 
1852
- specify "should not options such as server" do
2026
+ specify "should not remove non-SQL options such as :server" do
1853
2027
  @ds.server(:blah).from_self(:alias=>:some_name).opts[:server].should == :blah
1854
2028
  end
1855
2029
 
2030
+ specify "should hoist WITH clauses in current dataset if dataset doesn't support WITH in subselect" do
2031
+ ds = Sequel::Dataset.new(nil)
2032
+ ds.meta_def(:supports_cte?){true}
2033
+ ds.meta_def(:supports_cte_in_subselect?){false}
2034
+ 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'
2035
+ 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'
2036
+ end
1856
2037
  end
1857
2038
 
1858
2039
  describe "Dataset#join_table" do
1859
2040
  before do
1860
- @d = MockDataset.new(nil).from(:items)
2041
+ @d = Sequel::Dataset.new(nil).from(:items)
1861
2042
  @d.quote_identifiers = true
1862
2043
  end
1863
2044
 
1864
2045
  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")'
2046
+ @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
2047
  end
1868
2048
 
1869
2049
  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 ==
2050
+ @d.join_table(:left_outer, :categories, [[:category_id, :id], [:category_id, 0..100]]).sql.should ==
1871
2051
  'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON (("categories"."category_id" = "items"."id") AND ("categories"."category_id" >= 0) AND ("categories"."category_id" <= 100))'
1872
2052
  end
1873
2053
 
1874
2054
  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 ==
2055
+ @d.filter(Sequel.expr(:price) < 100).join_table(:right_outer, :categories, :category_id => :id).sql.should ==
1876
2056
  'SELECT * FROM "items" RIGHT OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id") WHERE ("price" < 100)'
1877
2057
  end
1878
2058
 
1879
2059
  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"'
2060
+ @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
2061
  end
1883
2062
 
1884
2063
  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")'
2064
+ @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
2065
  end
1888
2066
 
1889
2067
  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")'
2068
+ @d.join_table(:magic, :categories, :category_id=>:id).sql.should == 'SELECT * FROM "items" MAGIC JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1892
2069
  end
1893
2070
 
1894
2071
  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"'
2072
+ @d.left_outer_join(:categories, :category_id=>:id).sql.should == 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
2073
+ @d.right_outer_join(:categories, :category_id=>:id).sql.should == 'SELECT * FROM "items" RIGHT OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
2074
+ @d.full_outer_join(:categories, :category_id=>:id).sql.should == 'SELECT * FROM "items" FULL OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
2075
+ @d.inner_join(:categories, :category_id=>:id).sql.should == 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
2076
+ @d.left_join(:categories, :category_id=>:id).sql.should == 'SELECT * FROM "items" LEFT JOIN "categories" ON ("categories"."category_id" = "items"."id")'
2077
+ @d.right_join(:categories, :category_id=>:id).sql.should == 'SELECT * FROM "items" RIGHT JOIN "categories" ON ("categories"."category_id" = "items"."id")'
2078
+ @d.full_join(:categories, :category_id=>:id).sql.should == 'SELECT * FROM "items" FULL JOIN "categories" ON ("categories"."category_id" = "items"."id")'
2079
+ @d.natural_join(:categories).sql.should == 'SELECT * FROM "items" NATURAL JOIN "categories"'
2080
+ @d.natural_left_join(:categories).sql.should == 'SELECT * FROM "items" NATURAL LEFT JOIN "categories"'
2081
+ @d.natural_right_join(:categories).sql.should == 'SELECT * FROM "items" NATURAL RIGHT JOIN "categories"'
2082
+ @d.natural_full_join(:categories).sql.should == 'SELECT * FROM "items" NATURAL FULL JOIN "categories"'
2083
+ @d.cross_join(:categories).sql.should == 'SELECT * FROM "items" CROSS JOIN "categories"'
1919
2084
  end
1920
2085
 
1921
2086
  specify "should raise an error if additional arguments are provided to join methods that don't take conditions" do
@@ -1935,95 +2100,76 @@ describe "Dataset#join_table" do
1935
2100
  end
1936
2101
 
1937
2102
  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")'
2103
+ @d.join_table(nil, :categories, :category_id=>:id).sql.should == 'SELECT * FROM "items" JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1940
2104
  end
1941
2105
 
1942
2106
  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")'
2107
+ @d.join(:categories, :category_id=>:id).sql.should == 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1945
2108
  end
1946
2109
 
1947
2110
  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")'
2111
+ @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
2112
  end
1951
2113
 
1952
2114
  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")'
2115
+ @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
2116
  end
1956
2117
 
1957
2118
  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")'
2119
+ @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
2120
  end
1961
2121
 
1962
2122
  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")'
2123
+ @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
2124
  end
1968
2125
 
1969
2126
  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")'
2127
+ @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
2128
  end
1973
2129
 
1974
2130
  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")'
2131
+ @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")'
2132
+ @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
2133
  end
1980
2134
 
1981
2135
  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")'
2136
+ @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
2137
  end
1985
2138
 
1986
2139
  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")'
2140
+ @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")'
2141
+ end
2142
+
2143
+ specify "should not qualify if :qualify=>false option is given" do
2144
+ @d.from('stats').join(:players, {:id => :player_id}, :qualify=>false).sql.should == 'SELECT * FROM "stats" INNER JOIN "players" ON ("id" = "player_id")'
2145
+ end
2146
+
2147
+ specify "should do deep qualification if :qualify=>:deep option is given" do
2148
+ @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
2149
  end
1990
2150
 
1991
2151
  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))'
2152
+ @d.join_table(:left_outer, :categories, :status => 0).sql.should == 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."status" = 0)'
2153
+ @d.join_table(:left_outer, :categories, :categorizable_type => "Post").sql.should == 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."categorizable_type" = \'Post\')'
2154
+ @d.join_table(:left_outer, :categories, :timestamp => Sequel::CURRENT_TIMESTAMP).sql.should == 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."timestamp" = CURRENT_TIMESTAMP)'
2155
+ @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
2156
  end
2001
2157
 
2002
2158
  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)
2159
+ proc {Sequel::Dataset.new(nil).join('players', :id => :player_id)}.should raise_error(Sequel::Error)
2005
2160
  end
2006
2161
 
2007
2162
  specify "should support joining datasets" do
2008
2163
  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
-
2164
+ @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
2165
  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")'
2166
+ @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")'
2167
+ @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
2168
  end
2021
2169
 
2022
2170
  specify "should support joining datasets and aliasing the join" do
2023
2171
  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")'
2172
+ @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
2173
  end
2028
2174
 
2029
2175
  specify "should support joining multiple datasets" do
@@ -2037,43 +2183,27 @@ describe "Dataset#join_table" do
2037
2183
  'RIGHT OUTER JOIN (SELECT * FROM attributes WHERE (name = \'blah\')) AS "t3" ON ("t3"."attribute_id" = "t2"."id")'
2038
2184
  end
2039
2185
 
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")'
2186
+ specify "should support using an SQL String as the join condition" do
2187
+ @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)'
2046
2188
  end
2047
2189
 
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)'
2051
- end
2052
-
2053
- 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"'
2190
+ specify "should support using a boolean column as the join condition" do
2191
+ @d.join(:categories, :active).sql.should == 'SELECT * FROM "items" INNER JOIN "categories" ON "active"'
2056
2192
  end
2057
2193
 
2058
2194
  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)'
2195
+ @d.join(:categories, Sequel.expr(:number) > 10).sql.should == 'SELECT * FROM "items" INNER JOIN "categories" ON ("number" > 10)'
2061
2196
  end
2062
2197
 
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"'
2198
+ specify "should support natural and cross joins" do
2199
+ @d.join_table(:natural, :categories).sql.should == 'SELECT * FROM "items" NATURAL JOIN "categories"'
2200
+ @d.join_table(:cross, :categories, nil).sql.should == 'SELECT * FROM "items" CROSS JOIN "categories"'
2201
+ @d.join_table(:natural, :categories, nil, :c).sql.should == 'SELECT * FROM "items" NATURAL JOIN "categories" AS "c"'
2070
2202
  end
2071
2203
 
2072
2204
  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")'
2205
+ @d.join(:categories, [:id]).sql.should == 'SELECT * FROM "items" INNER JOIN "categories" USING ("id")'
2206
+ @d.join(:categories, [:id1, :id2]).sql.should == 'SELECT * FROM "items" INNER JOIN "categories" USING ("id1", "id2")'
2077
2207
  end
2078
2208
 
2079
2209
  specify "should emulate JOIN USING (poorly) if the dataset doesn't support it" do
@@ -2081,6 +2211,12 @@ describe "Dataset#join_table" do
2081
2211
  @d.join(:categories, [:id]).sql.should == 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."id" = "items"."id")'
2082
2212
  end
2083
2213
 
2214
+ specify "should hoist WITH clauses from subqueries if the dataset doesn't support CTEs in subselects" do
2215
+ @d.meta_def(:supports_cte?){true}
2216
+ @d.meta_def(:supports_cte_in_subselect?){false}
2217
+ @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")'
2218
+ end
2219
+
2084
2220
  specify "should raise an error if using an array of symbols with a block" do
2085
2221
  proc{@d.join(:categories, [:id]){|j,lj,js|}}.should raise_error(Sequel::Error)
2086
2222
  end
@@ -2135,27 +2271,21 @@ describe "Dataset#join_table" do
2135
2271
  end
2136
2272
 
2137
2273
  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")'
2274
+ @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")'
2275
+ @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
2276
  end
2143
2277
 
2144
2278
  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"))'
2279
+ @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"))'
2280
+ @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
2281
  end
2150
2282
 
2151
2283
  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 ==
2284
+ @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")'
2285
+ @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
2286
  'SELECT * FROM "items" AS "i" INNER JOIN "categories" AS "c2" ON ("c2"."category_id" = "i2"."id")'
2156
2287
  end
2157
2288
 
2158
-
2159
2289
  specify "should not allow insert, update, delete, or truncate" do
2160
2290
  proc{@d.join(:categories, :a=>:d).insert_sql}.should raise_error(Sequel::InvalidOperation)
2161
2291
  proc{@d.join(:categories, :a=>:d).update_sql(:a=>1)}.should raise_error(Sequel::InvalidOperation)
@@ -2165,86 +2295,64 @@ describe "Dataset#join_table" do
2165
2295
 
2166
2296
  specify "should raise an error if an invalid option is passed" do
2167
2297
  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)
2298
+ proc{@d.join(:c, [:id], Sequel.qualify(:d, :c))}.should raise_error(Sequel::Error)
2169
2299
  end
2170
2300
  end
2171
2301
 
2172
2302
  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
2303
  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)'
2304
+ db = Sequel.mock
2305
+ ds = db[:items]
2306
+ ds[:a => 1] = {:x => 3}
2307
+ db.sqls.should == ['UPDATE items SET x = 3 WHERE (a = 1)']
2190
2308
  end
2191
2309
  end
2192
2310
 
2193
2311
  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
2312
  specify "should act as alias to #update" do
2209
- @d.set({:x => 3})
2210
- @d.last_sql.should == 'UPDATE items SET x = 3'
2313
+ db = Sequel.mock
2314
+ ds = db[:items]
2315
+ ds.set({:x => 3})
2316
+ db.sqls.should == ['UPDATE items SET x = 3']
2211
2317
  end
2212
2318
  end
2213
2319
 
2214
-
2215
2320
  describe "Dataset#insert_multiple" do
2216
2321
  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)
2322
+ @db = Sequel.mock(:autoid=>2)
2323
+ @ds = @db[:items]
2226
2324
  end
2227
2325
 
2228
2326
  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}]
2327
+ @ds.insert_multiple(['aa', 5, 3, {:a => 2}])
2328
+ @db.sqls.should == ["INSERT INTO items VALUES ('aa')",
2329
+ "INSERT INTO items VALUES (5)",
2330
+ "INSERT INTO items VALUES (3)",
2331
+ "INSERT INTO items (a) VALUES (2)"]
2231
2332
  end
2232
2333
 
2233
2334
  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"]
2335
+ @ds.insert_multiple(["inevitable", "hello", "the ticking clock"]){|i| i.gsub('l', 'r')}
2336
+ @db.sqls.should == ["INSERT INTO items VALUES ('inevitabre')",
2337
+ "INSERT INTO items VALUES ('herro')",
2338
+ "INSERT INTO items VALUES ('the ticking crock')"]
2339
+ end
2340
+
2341
+ specify "should return array of inserted ids" do
2342
+ @ds.insert_multiple(['aa', 5, 3, {:a => 2}]).should == [2, 3, 4, 5]
2343
+ end
2344
+
2345
+ specify "should work exactly like in metioned in the example" do
2346
+ @ds.insert_multiple([{:x=>1}, {:x=>2}]){|row| row[:y] = row[:x] * 2 ; row }
2347
+ sqls = @db.sqls
2348
+ ["INSERT INTO items (x, y) VALUES (1, 2)", "INSERT INTO items (y, x) VALUES (2, 1)"].should include(sqls[0])
2349
+ ["INSERT INTO items (x, y) VALUES (2, 4)", "INSERT INTO items (y, x) VALUES (4, 2)"].should include(sqls[1])
2237
2350
  end
2238
2351
  end
2239
2352
 
2240
2353
  describe "Dataset aggregate methods" do
2241
2354
  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)
2355
+ @d = Sequel.mock(:fetch=>proc{|s| {1=>s}})[:test]
2248
2356
  end
2249
2357
 
2250
2358
  specify "should include min" do
@@ -2278,116 +2386,88 @@ end
2278
2386
 
2279
2387
  describe "Dataset#range" do
2280
2388
  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)
2389
+ @db = Sequel.mock(:fetch=>{:v1 => 1, :v2 => 10})
2390
+ @ds = @db[:test]
2292
2391
  end
2293
2392
 
2294
2393
  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"
2394
+ @ds.range(:stamp)
2395
+ @db.sqls.should == ["SELECT min(stamp) AS v1, max(stamp) AS v2 FROM test LIMIT 1"]
2297
2396
 
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"
2397
+ @ds.filter(Sequel.expr(:price) > 100).range(:stamp)
2398
+ @db.sqls.should == ["SELECT min(stamp) AS v1, max(stamp) AS v2 FROM test WHERE (price > 100) LIMIT 1"]
2300
2399
  end
2301
2400
 
2302
2401
  specify "should return a range object" do
2303
- @d.range(:tryme).should == (1..10)
2402
+ @ds.range(:tryme).should == (1..10)
2304
2403
  end
2305
2404
 
2306
2405
  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'
2406
+ @ds.order(:stamp).limit(5).range(:stamp).should == (1..10)
2407
+ @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
2408
  end
2310
2409
  end
2311
2410
 
2312
2411
  describe "Dataset#interval" do
2313
2412
  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)
2413
+ @db = Sequel.mock(:fetch=>{:v => 1234})
2414
+ @ds = @db[:test]
2325
2415
  end
2326
2416
 
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"
2417
+ specify "should generate the correct SQL statement" do
2418
+ @ds.interval(:stamp)
2419
+ @db.sqls.should == ["SELECT (max(stamp) - min(stamp)) FROM test LIMIT 1"]
2330
2420
 
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
2421
+ @ds.filter(Sequel.expr(:price) > 100).interval(:stamp)
2422
+ @db.sqls.should == ["SELECT (max(stamp) - min(stamp)) FROM test WHERE (price > 100) LIMIT 1"]
2337
2423
  end
2338
2424
 
2339
2425
  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'
2426
+ @ds.order(:stamp).limit(5).interval(:stamp).should == 1234
2427
+ @db.sqls.should == ['SELECT (max(stamp) - min(stamp)) FROM (SELECT * FROM test ORDER BY stamp LIMIT 5) AS t1 LIMIT 1']
2342
2428
  end
2343
2429
  end
2344
2430
 
2345
2431
  describe "Dataset #first and #last" do
2346
2432
  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)
2433
+ @db = Sequel.mock(:fetch=>proc{|s| {:s=>s}})
2434
+ @d = @db[:test]
2355
2435
  end
2356
2436
 
2357
2437
  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']
2438
+ @d.order(:a).first.should == {:s=>'SELECT * FROM test ORDER BY a LIMIT 1'}
2439
+ @d.order(:a).last.should == {:s=>'SELECT * FROM test ORDER BY a DESC LIMIT 1'}
2360
2440
  end
2361
2441
 
2362
2442
  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']
2443
+ @d.order(:a).first(:z => 26).should == {:s=>'SELECT * FROM test WHERE (z = 26) ORDER BY a LIMIT 1'}
2444
+ @d.order(:a).first('z = ?', 15).should == {:s=>'SELECT * FROM test WHERE (z = 15) ORDER BY a LIMIT 1'}
2445
+ @d.order(:a).last(:z => 26).should == {:s=>'SELECT * FROM test WHERE (z = 26) ORDER BY a DESC LIMIT 1'}
2446
+ @d.order(:a).last('z = ?', 15).should == {:s=>'SELECT * FROM test WHERE (z = 15) ORDER BY a DESC LIMIT 1'}
2367
2447
  end
2368
2448
 
2369
2449
  specify "should set the limit and return an array of records if the given number is > 1" do
2370
2450
  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
2451
+ r = @d.order(:a).first(i).should == [{:s=>"SELECT * FROM test ORDER BY a LIMIT #{i}"}]
2372
2452
  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
2453
+ r = @d.order(:a).last(i).should == [{:s=>"SELECT * FROM test ORDER BY a DESC LIMIT #{i}"}]
2374
2454
  end
2375
2455
 
2376
2456
  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']
2457
+ @d.first{z > 26}.should == {:s=>'SELECT * FROM test WHERE (z > 26) LIMIT 1'}
2458
+ @d.order(:name).last{z > 26}.should == {:s=>'SELECT * FROM test WHERE (z > 26) ORDER BY name DESC LIMIT 1'}
2379
2459
  end
2380
2460
 
2381
2461
  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']
2462
+ @d.first(:y=>25){z > 26}.should == {:s=>'SELECT * FROM test WHERE ((z > 26) AND (y = 25)) LIMIT 1'}
2463
+ @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
2464
  end
2385
2465
 
2386
2466
  specify "should filter and return an array of records if an Integer argument is provided and a block is given" do
2387
2467
  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
2468
+ r = @d.order(:a).first(i){z > 26}.should == [{:s=>"SELECT * FROM test WHERE (z > 26) ORDER BY a LIMIT #{i}"}]
2389
2469
  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
2470
+ r = @d.order(:a).last(i){z > 26}.should == [{:s=>"SELECT * FROM test WHERE (z > 26) ORDER BY a DESC LIMIT #{i}"}]
2391
2471
  end
2392
2472
 
2393
2473
  specify "#last should raise if no order is given" do
@@ -2398,10 +2478,10 @@ describe "Dataset #first and #last" do
2398
2478
  end
2399
2479
 
2400
2480
  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'
2481
+ @d.order(:a).last.should == {:s=>'SELECT * FROM test ORDER BY a DESC LIMIT 1'}
2482
+ @d.order(Sequel.desc(:b)).last.should == {:s=>'SELECT * FROM test ORDER BY b ASC LIMIT 1'}
2483
+ @d.order(:c, :d).last.should == {:s=>'SELECT * FROM test ORDER BY c DESC, d DESC LIMIT 1'}
2484
+ @d.order(Sequel.desc(:e), :f).last.should == {:s=>'SELECT * FROM test ORDER BY e ASC, f DESC LIMIT 1'}
2405
2485
  end
2406
2486
  end
2407
2487
 
@@ -2412,55 +2492,37 @@ describe "Dataset compound operations" do
2412
2492
  end
2413
2493
 
2414
2494
  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"
2495
+ @a.union(@b).sql.should == "SELECT * FROM (SELECT * FROM a WHERE (z = 1) UNION SELECT * FROM b WHERE (z = 2)) AS t1"
2496
+ @b.union(@a, true).sql.should == "SELECT * FROM (SELECT * FROM b WHERE (z = 2) UNION ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2497
+ @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
2498
  end
2422
2499
 
2423
2500
  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"
2501
+ @a.intersect(@b).sql.should == "SELECT * FROM (SELECT * FROM a WHERE (z = 1) INTERSECT SELECT * FROM b WHERE (z = 2)) AS t1"
2502
+ @b.intersect(@a, true).sql.should == "SELECT * FROM (SELECT * FROM b WHERE (z = 2) INTERSECT ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2503
+ @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
2504
  end
2431
2505
 
2432
2506
  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"
2507
+ @a.except(@b).sql.should == "SELECT * FROM (SELECT * FROM a WHERE (z = 1) EXCEPT SELECT * FROM b WHERE (z = 2)) AS t1"
2508
+ @b.except(@a, true).sql.should == "SELECT * FROM (SELECT * FROM b WHERE (z = 2) EXCEPT ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2509
+ @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
2510
  end
2440
2511
 
2441
2512
  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"
2513
+ @a.union(@b, :alias=>:xx).sql.should == "SELECT * FROM (SELECT * FROM a WHERE (z = 1) UNION SELECT * FROM b WHERE (z = 2)) AS xx"
2514
+ @a.intersect(@b, :alias=>:xx).sql.should == "SELECT * FROM (SELECT * FROM a WHERE (z = 1) INTERSECT SELECT * FROM b WHERE (z = 2)) AS xx"
2515
+ @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
2516
  end
2449
2517
 
2450
2518
  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)"
2519
+ @b.union(@a, :from_self=>false).sql.should == "SELECT * FROM b WHERE (z = 2) UNION SELECT * FROM a WHERE (z = 1)"
2520
+ @b.intersect(@a, :from_self=>false).sql.should == "SELECT * FROM b WHERE (z = 2) INTERSECT SELECT * FROM a WHERE (z = 1)"
2521
+ @b.except(@a, :from_self=>false).sql.should == "SELECT * FROM b WHERE (z = 2) EXCEPT SELECT * FROM a WHERE (z = 1)"
2457
2522
 
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)"
2523
+ @b.union(@a, :from_self=>false, :all=>true).sql.should == "SELECT * FROM b WHERE (z = 2) UNION ALL SELECT * FROM a WHERE (z = 1)"
2524
+ @b.intersect(@a, :from_self=>false, :all=>true).sql.should == "SELECT * FROM b WHERE (z = 2) INTERSECT ALL SELECT * FROM a WHERE (z = 1)"
2525
+ @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
2526
  end
2465
2527
 
2466
2528
  specify "should raise an InvalidOperation if INTERSECT or EXCEPT is used and they are not supported" do
@@ -2480,141 +2542,105 @@ describe "Dataset compound operations" do
2480
2542
  end
2481
2543
 
2482
2544
  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"
2545
+ @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"
2546
+ @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"
2547
+ @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
2548
  end
2490
2549
 
2491
2550
  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"
2551
+ @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"
2552
+ @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"
2553
+ @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
2554
  end
2499
2555
 
2500
2556
  specify "should order and limit properly when using UNION, INTERSECT, or EXCEPT" do
2501
2557
  @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"
2558
+ @dataset.union(@dataset).limit(2).sql.should == "SELECT * FROM (SELECT * FROM test UNION SELECT * FROM test) AS t1 LIMIT 2"
2559
+ @dataset.limit(2).intersect(@dataset).sql.should == "SELECT * FROM (SELECT * FROM (SELECT * FROM test LIMIT 2) AS t1 INTERSECT SELECT * FROM test) AS t1"
2560
+ @dataset.except(@dataset.limit(2)).sql.should == "SELECT * FROM (SELECT * FROM test EXCEPT SELECT * FROM (SELECT * FROM test LIMIT 2) AS t1) AS t1"
2561
+
2562
+ @dataset.union(@dataset).order(:num).sql.should == "SELECT * FROM (SELECT * FROM test UNION SELECT * FROM test) AS t1 ORDER BY num"
2563
+ @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"
2564
+ @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
2565
 
2516
2566
  @dataset.limit(2).order(:a).union(@dataset.limit(3).order(:b)).order(:c).limit(4).sql.should ==
2517
2567
  "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
2568
  end
2519
2569
 
2570
+ specify "should hoist WITH clauses in given dataset if dataset doesn't support WITH in subselect" do
2571
+ ds = Sequel::Dataset.new(nil)
2572
+ ds.meta_def(:supports_cte?){true}
2573
+ ds.meta_def(:supports_cte_in_subselect?){false}
2574
+ 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'
2575
+ 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'
2576
+ 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'
2577
+ end
2520
2578
  end
2521
2579
 
2522
2580
  describe "Dataset#[]" do
2523
2581
  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)
2582
+ @db = Sequel.mock(:fetch=>{1 => 2, 3 => 4})
2583
+ @d = @db[:items]
2537
2584
  end
2538
2585
 
2539
2586
  specify "should return a single record filtered according to the given conditions" do
2540
2587
  @d[:name => 'didi'].should == {1 => 2, 3 => 4}
2541
- @c.last_dataset.literal(@c.last_dataset.opts[:where]).should == "(name = 'didi')"
2588
+ @db.sqls.should == ["SELECT * FROM items WHERE (name = 'didi') LIMIT 1"]
2542
2589
 
2543
2590
  @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))"
2591
+ @db.sqls.should == ["SELECT * FROM items WHERE ((id >= 5) AND (id <= 45)) LIMIT 1"]
2545
2592
  end
2546
2593
  end
2547
2594
 
2548
2595
  describe "Dataset#single_record" do
2549
2596
  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)
2597
+ @db = Sequel.mock
2561
2598
  end
2562
2599
 
2563
2600
  specify "should call each with a limit of 1 and return the record" do
2564
- @d.single_record.should == 'SELECT * FROM test LIMIT 1'
2601
+ @db.fetch = {:a=>1}
2602
+ @db[:test].single_record.should == {:a=>1}
2603
+ @db.sqls.should == ['SELECT * FROM test LIMIT 1']
2565
2604
  end
2566
2605
 
2567
2606
  specify "should return nil if no record is present" do
2568
- @e.single_record.should be_nil
2607
+ @db[:test].single_record.should be_nil
2608
+ @db.sqls.should == ['SELECT * FROM test LIMIT 1']
2569
2609
  end
2570
2610
  end
2571
2611
 
2572
2612
  describe "Dataset#single_value" do
2573
2613
  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)
2614
+ @db = Sequel.mock
2585
2615
  end
2586
2616
 
2587
2617
  specify "should call each and return the first value of the first record" do
2588
- @d.single_value.should == 'SELECT * FROM test LIMIT 1'
2618
+ @db.fetch = {:a=>1}
2619
+ @db[:test].single_value.should == 1
2620
+ @db.sqls.should == ['SELECT * FROM test LIMIT 1']
2589
2621
  end
2590
2622
 
2591
2623
  specify "should return nil if no records" do
2592
- @e.single_value.should be_nil
2624
+ @db[:test].single_value.should be_nil
2625
+ @db.sqls.should == ['SELECT * FROM test LIMIT 1']
2593
2626
  end
2594
2627
 
2595
2628
  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'
2629
+ @db.fetch = {:a=>1}
2630
+ ds = @db[:test].columns(:a)
2631
+ ds.graph(ds, [:a], :table_alias=>:test2).single_value.should == 1
2632
+ @db.sqls.should == ['SELECT test.a, test2.a AS test2_a FROM test LEFT OUTER JOIN test AS test2 USING (a) LIMIT 1']
2598
2633
  end
2599
2634
  end
2600
2635
 
2601
2636
  describe "Dataset#get" do
2602
2637
  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)
2638
+ @d = Sequel.mock(:fetch=>proc{|s| {:name=>s}})[:test]
2613
2639
  end
2614
2640
 
2615
2641
  specify "should select the specified column and fetch its value" do
2616
2642
  @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
2643
+ @d.get(:abc).should == "SELECT abc FROM test LIMIT 1"
2618
2644
  end
2619
2645
 
2620
2646
  specify "should work with filters" do
@@ -2622,7 +2648,7 @@ describe "Dataset#get" do
2622
2648
  end
2623
2649
 
2624
2650
  specify "should work with aliased fields" do
2625
- @d.get(:x__b.as(:name)).should == "SELECT x.b AS name FROM test LIMIT 1"
2651
+ @d.get(Sequel.expr(:x__b).as(:name)).should == "SELECT x.b AS name FROM test LIMIT 1"
2626
2652
  end
2627
2653
 
2628
2654
  specify "should accept a block that yields a virtual row" do
@@ -2633,243 +2659,253 @@ describe "Dataset#get" do
2633
2659
  specify "should raise an error if both a regular argument and block argument are used" do
2634
2660
  proc{@d.get(:name){|o| o.x__b.as(:name)}}.should raise_error(Sequel::Error)
2635
2661
  end
2662
+
2663
+ specify "should support false and nil values" do
2664
+ @d.get(false).should == "SELECT 'f' FROM test LIMIT 1"
2665
+ @d.get(nil).should == "SELECT NULL FROM test LIMIT 1"
2666
+ end
2636
2667
  end
2637
2668
 
2638
2669
  describe "Dataset#set_row_proc" do
2639
2670
  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)
2671
+ @db = Sequel.mock(:fetch=>[{:a=>1}, {:a=>2}])
2672
+ @dataset = @db[:items]
2673
+ @dataset.row_proc = proc{|h| h[:der] = h[:a] + 2; h}
2647
2674
  end
2648
2675
 
2649
2676
  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
2677
  rows = @dataset.all
2653
- rows.size.should == 10
2654
-
2655
- rows.each {|r| r[:der].should == (r[:kind] + 2)}
2678
+ rows.map{|h| h[:der]}.should == [3, 4]
2679
+ @db.sqls.should == ['SELECT * FROM items']
2656
2680
  end
2657
2681
 
2658
2682
  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}
2683
+ @dataset.filter(:a => 1).all.should == [{:a=>1, :der=>3}, {:a=>2, :der=>4}]
2662
2684
  end
2663
2685
  end
2664
2686
 
2665
2687
  describe "Dataset#<<" do
2666
2688
  before do
2667
- @d = Sequel::Dataset.new(nil)
2668
- @d.meta_def(:insert) do |*args|
2669
- 1234567890
2670
- end
2689
+ @db = Sequel.mock
2671
2690
  end
2672
-
2691
+
2673
2692
  specify "should call #insert" do
2674
- (@d << {:name => 1}).should == 1234567890
2693
+ @db[:items] << {:name => 1}
2694
+ @db.sqls.should == ['INSERT INTO items (name) VALUES (1)']
2695
+ end
2696
+
2697
+ specify "should be chainable" do
2698
+ @db[:items] << {:name => 1} << @db[:old_items].select(:name)
2699
+ @db.sqls.should == ['INSERT INTO items (name) VALUES (1)', 'INSERT INTO items SELECT name FROM old_items']
2675
2700
  end
2676
2701
  end
2677
2702
 
2678
2703
  describe "Dataset#columns" do
2679
2704
  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}
2705
+ @dataset = Sequel.mock[:items]
2684
2706
  end
2685
2707
 
2686
2708
  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]
2709
+ @dataset.columns(:a, :b, :c).columns.should == [:a, :b, :c]
2710
+ @dataset.db.sqls.should == []
2689
2711
  end
2690
2712
 
2691
2713
  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'
2714
+ @dataset.db.columns = [:a]
2715
+ @dataset.columns.should == [:a]
2716
+ @dataset.db.sqls.should == ['SELECT * FROM items LIMIT 1']
2717
+ end
2718
+
2719
+ specify "should be cleared if you change the selected columns" do
2720
+ @dataset.db.columns = [[:a], [:b]]
2721
+ @dataset.columns.should == [:a]
2722
+ @dataset.db.sqls.should == ['SELECT * FROM items LIMIT 1']
2723
+ @dataset.columns.should == [:a]
2724
+ @dataset.db.sqls.should == []
2725
+ ds = @dataset.select{foo{}}
2726
+ ds.columns.should == [:b]
2727
+ @dataset.db.sqls.should == ['SELECT foo() FROM items LIMIT 1']
2728
+ end
2729
+
2730
+ specify "should be cleared if you change the FROM table" do
2731
+ @dataset.db.columns = [[:a], [:b]]
2732
+ @dataset.columns.should == [:a]
2733
+ @dataset.db.sqls.should == ['SELECT * FROM items LIMIT 1']
2734
+ ds = @dataset.from(:foo)
2735
+ ds.columns.should == [:b]
2736
+ @dataset.db.sqls.should == ['SELECT * FROM foo LIMIT 1']
2737
+ end
2738
+
2739
+ specify "should be cleared if you join a table to the dataset" do
2740
+ @dataset.db.columns = [[:a], [:a, :b]]
2741
+ @dataset.columns.should == [:a]
2742
+ @dataset.db.sqls.should == ['SELECT * FROM items LIMIT 1']
2743
+ ds = @dataset.cross_join(:foo)
2744
+ ds.columns.should == [:a, :b]
2745
+ @dataset.db.sqls.should == ['SELECT * FROM items CROSS JOIN foo LIMIT 1']
2746
+ end
2747
+
2748
+ specify "should be cleared if you set custom SQL for the dataset" do
2749
+ @dataset.db.columns = [[:a], [:b]]
2750
+ @dataset.columns.should == [:a]
2751
+ @dataset.db.sqls.should == ['SELECT * FROM items LIMIT 1']
2752
+ ds = @dataset.with_sql('SELECT b FROM foo')
2753
+ ds.columns.should == [:b]
2754
+ @dataset.db.sqls.should == ['SELECT b FROM foo']
2696
2755
  end
2697
2756
 
2698
2757
  specify "should ignore any filters, orders, or DISTINCT clauses" do
2758
+ @dataset.db.columns = [:a]
2699
2759
  @dataset.filter!(:b=>100).order!(:b).distinct!
2700
- @dataset.columns = nil
2701
- @dataset.columns.should == 'SELECT * FROM items LIMIT 1a'
2760
+ @dataset.columns.should == [:a]
2761
+ @dataset.db.sqls.should == ['SELECT * FROM items LIMIT 1']
2702
2762
  end
2703
2763
  end
2704
2764
 
2705
2765
  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
2766
  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'
2767
+ ds = Sequel.mock(:columns=>[[:a, :b, :c], [:d, :e, :f]])[:items]
2768
+ ds.columns!.should == [:a, :b, :c]
2769
+ ds.db.sqls.should == ['SELECT * FROM items LIMIT 1']
2770
+ ds.columns!.should == [:d, :e, :f]
2771
+ ds.db.sqls.should == ['SELECT * FROM items LIMIT 1']
2717
2772
  end
2718
2773
  end
2719
2774
 
2720
2775
  describe "Dataset#import" do
2721
2776
  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'}]
2777
+ @db = Sequel.mock
2778
+ @ds = @db[:items]
2743
2779
  end
2744
2780
 
2781
+ specify "should return nil without a query if no values" do
2782
+ @ds.import(['x', 'y'], []).should == nil
2783
+ @db.sqls.should == []
2784
+ end
2785
+
2745
2786
  specify "should accept string keys as column names" do
2746
2787
  @ds.import(['x', 'y'], [[1, 2], [3, 4]])
2747
- @db.sqls.should == [
2748
- 'BEGIN',
2788
+ @db.sqls.should == ['BEGIN',
2749
2789
  "INSERT INTO items (x, y) VALUES (1, 2)",
2750
2790
  "INSERT INTO items (x, y) VALUES (3, 4)",
2751
- 'COMMIT'
2752
- ]
2791
+ 'COMMIT']
2753
2792
  end
2754
2793
 
2755
2794
  specify "should accept a columns array and a values array" do
2756
2795
  @ds.import([:x, :y], [[1, 2], [3, 4]])
2757
- @db.sqls.should == [
2758
- 'BEGIN',
2796
+ @db.sqls.should == ['BEGIN',
2759
2797
  "INSERT INTO items (x, y) VALUES (1, 2)",
2760
2798
  "INSERT INTO items (x, y) VALUES (3, 4)",
2761
- 'COMMIT'
2762
- ]
2799
+ 'COMMIT']
2763
2800
  end
2764
2801
 
2765
2802
  specify "should accept a columns array and a dataset" do
2766
- @ds2 = Sequel::Dataset.new(@db).from(:cats).filter(:purr => true).select(:a, :b)
2803
+ @ds2 = @ds.from(:cats).filter(:purr => true).select(:a, :b)
2767
2804
 
2768
2805
  @ds.import([:x, :y], @ds2)
2769
- @db.sqls.should == [
2770
- 'BEGIN',
2806
+ @db.sqls.should == ['BEGIN',
2771
2807
  "INSERT INTO items (x, y) SELECT a, b FROM cats WHERE (purr IS TRUE)",
2772
- 'COMMIT'
2773
- ]
2808
+ 'COMMIT']
2774
2809
  end
2775
2810
 
2776
2811
  specify "should accept a columns array and a values array with :commit_every option" do
2777
2812
  @ds.import([:x, :y], [[1, 2], [3, 4], [5, 6]], :commit_every => 3)
2778
- @db.sqls.should == [
2779
- 'BEGIN',
2813
+ @db.sqls.should == ['BEGIN',
2780
2814
  "INSERT INTO items (x, y) VALUES (1, 2)",
2781
2815
  "INSERT INTO items (x, y) VALUES (3, 4)",
2782
2816
  "INSERT INTO items (x, y) VALUES (5, 6)",
2783
- 'COMMIT',
2784
- ]
2817
+ 'COMMIT']
2785
2818
  end
2786
- specify "should accept a columns array and a values array with slice option" do
2819
+ specify "should accept a columns array and a values array with :slice option" do
2787
2820
  @ds.import([:x, :y], [[1, 2], [3, 4], [5, 6]], :slice => 2)
2788
- @db.sqls.should == [
2789
- 'BEGIN',
2821
+ @db.sqls.should == ['BEGIN',
2790
2822
  "INSERT INTO items (x, y) VALUES (1, 2)",
2791
2823
  "INSERT INTO items (x, y) VALUES (3, 4)",
2792
2824
  'COMMIT',
2793
2825
  'BEGIN',
2794
2826
  "INSERT INTO items (x, y) VALUES (5, 6)",
2795
- 'COMMIT'
2796
- ]
2827
+ 'COMMIT']
2797
2828
  end
2798
2829
  end
2799
2830
 
2800
2831
  describe "Dataset#multi_insert" do
2801
2832
  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
-
2833
+ @db = Sequel.mock(:servers=>{:s1=>{}})
2834
+ @ds = @db[:items]
2822
2835
  @list = [{:name => 'abc'}, {:name => 'def'}, {:name => 'ghi'}]
2823
2836
  end
2824
2837
 
2838
+ specify "should return nil without a query if no values" do
2839
+ @ds.multi_insert([]).should == nil
2840
+ @db.sqls.should == []
2841
+ end
2842
+
2825
2843
  specify "should issue multiple insert statements inside a transaction" do
2826
2844
  @ds.multi_insert(@list)
2827
- @db.sqls.should == [
2828
- 'BEGIN',
2845
+ @db.sqls.should == ['BEGIN',
2846
+ "INSERT INTO items (name) VALUES ('abc')",
2847
+ "INSERT INTO items (name) VALUES ('def')",
2848
+ "INSERT INTO items (name) VALUES ('ghi')",
2849
+ 'COMMIT']
2850
+ end
2851
+
2852
+ specify "should respect :server option" do
2853
+ @ds.multi_insert(@list, :server=>:s1)
2854
+ @db.sqls.should == ['BEGIN -- s1',
2855
+ "INSERT INTO items (name) VALUES ('abc') -- s1",
2856
+ "INSERT INTO items (name) VALUES ('def') -- s1",
2857
+ "INSERT INTO items (name) VALUES ('ghi') -- s1",
2858
+ 'COMMIT -- s1']
2859
+ end
2860
+
2861
+ specify "should respect existing :server option on dataset" do
2862
+ @ds.server(:s1).multi_insert(@list)
2863
+ @db.sqls.should == ['BEGIN -- s1',
2864
+ "INSERT INTO items (name) VALUES ('abc') -- s1",
2865
+ "INSERT INTO items (name) VALUES ('def') -- s1",
2866
+ "INSERT INTO items (name) VALUES ('ghi') -- s1",
2867
+ 'COMMIT -- s1']
2868
+ end
2869
+
2870
+ specify "should respect :return=>:primary_key option" do
2871
+ @db.autoid = 1
2872
+ @ds.multi_insert(@list, :return=>:primary_key).should == [1, 2, 3]
2873
+ @db.sqls.should == ['BEGIN',
2829
2874
  "INSERT INTO items (name) VALUES ('abc')",
2830
2875
  "INSERT INTO items (name) VALUES ('def')",
2831
2876
  "INSERT INTO items (name) VALUES ('ghi')",
2832
- 'COMMIT'
2833
- ]
2877
+ 'COMMIT']
2834
2878
  end
2835
2879
 
2836
2880
  specify "should handle different formats for tables" do
2837
2881
  @ds = @ds.from(:sch__tab)
2838
2882
  @ds.multi_insert(@list)
2839
- @db.sqls.should == [
2840
- 'BEGIN',
2883
+ @db.sqls.should == ['BEGIN',
2841
2884
  "INSERT INTO sch.tab (name) VALUES ('abc')",
2842
2885
  "INSERT INTO sch.tab (name) VALUES ('def')",
2843
2886
  "INSERT INTO sch.tab (name) VALUES ('ghi')",
2844
- 'COMMIT'
2845
- ]
2846
- @db.sqls.clear
2887
+ 'COMMIT']
2847
2888
 
2848
- @ds = @ds.from(:tab.qualify(:sch))
2889
+ @ds = @ds.from(Sequel.qualify(:sch, :tab))
2849
2890
  @ds.multi_insert(@list)
2850
- @db.sqls.should == [
2851
- 'BEGIN',
2891
+ @db.sqls.should == ['BEGIN',
2852
2892
  "INSERT INTO sch.tab (name) VALUES ('abc')",
2853
2893
  "INSERT INTO sch.tab (name) VALUES ('def')",
2854
2894
  "INSERT INTO sch.tab (name) VALUES ('ghi')",
2855
- 'COMMIT'
2856
- ]
2857
- @db.sqls.clear
2858
- @ds = @ds.from(:sch__tab.identifier)
2895
+ 'COMMIT']
2896
+
2897
+ @ds = @ds.from(Sequel.identifier(:sch__tab))
2859
2898
  @ds.multi_insert(@list)
2860
- @db.sqls.should == [
2861
- 'BEGIN',
2899
+ @db.sqls.should == ['BEGIN',
2862
2900
  "INSERT INTO sch__tab (name) VALUES ('abc')",
2863
2901
  "INSERT INTO sch__tab (name) VALUES ('def')",
2864
2902
  "INSERT INTO sch__tab (name) VALUES ('ghi')",
2865
- 'COMMIT'
2866
- ]
2903
+ 'COMMIT']
2867
2904
  end
2868
2905
 
2869
2906
  specify "should accept the :commit_every option for committing every x records" do
2870
2907
  @ds.multi_insert(@list, :commit_every => 1)
2871
- @db.sqls.should == [
2872
- 'BEGIN',
2908
+ @db.sqls.should == ['BEGIN',
2873
2909
  "INSERT INTO items (name) VALUES ('abc')",
2874
2910
  'COMMIT',
2875
2911
  'BEGIN',
@@ -2877,36 +2913,31 @@ describe "Dataset#multi_insert" do
2877
2913
  'COMMIT',
2878
2914
  'BEGIN',
2879
2915
  "INSERT INTO items (name) VALUES ('ghi')",
2880
- 'COMMIT'
2881
- ]
2916
+ 'COMMIT']
2882
2917
  end
2883
2918
 
2884
2919
  specify "should accept the :slice option for committing every x records" do
2885
2920
  @ds.multi_insert(@list, :slice => 2)
2886
- @db.sqls.should == [
2887
- 'BEGIN',
2921
+ @db.sqls.should == ['BEGIN',
2888
2922
  "INSERT INTO items (name) VALUES ('abc')",
2889
2923
  "INSERT INTO items (name) VALUES ('def')",
2890
2924
  'COMMIT',
2891
2925
  'BEGIN',
2892
2926
  "INSERT INTO items (name) VALUES ('ghi')",
2893
- 'COMMIT'
2894
- ]
2927
+ 'COMMIT']
2895
2928
  end
2896
2929
 
2897
2930
  specify "should accept string keys as column names" do
2898
2931
  @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
- ]
2932
+ sqls = @db.sqls
2933
+ ["INSERT INTO items (x, y) VALUES (1, 2)", "INSERT INTO items (y, x) VALUES (2, 1)"].should include(sqls.slice!(1))
2934
+ ["INSERT INTO items (x, y) VALUES (3, 4)", "INSERT INTO items (y, x) VALUES (4, 3)"].should include(sqls.slice!(1))
2935
+ sqls.should == ['BEGIN', 'COMMIT']
2905
2936
  end
2906
2937
 
2907
2938
  specify "should not do anything if no hashes are provided" do
2908
2939
  @ds.multi_insert([])
2909
- @db.sqls.should be_nil
2940
+ @db.sqls.should == []
2910
2941
  end
2911
2942
  end
2912
2943
 
@@ -2936,7 +2967,7 @@ describe "Dataset" do
2936
2967
  end
2937
2968
 
2938
2969
  specify "should support self-changing filter! with block" do
2939
- @d.filter!{:y.sql_number < 2}
2970
+ @d.filter!{y < 2}
2940
2971
  @d.sql.should == "SELECT * FROM x WHERE (y < 2)"
2941
2972
  end
2942
2973
 
@@ -2951,41 +2982,21 @@ describe "Dataset" do
2951
2982
  end
2952
2983
 
2953
2984
  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"
2985
+ @d.order!(:y).filter!(:y => 1).sql.should == "SELECT * FROM x WHERE (y = 1) ORDER BY y"
2957
2986
  end
2958
2987
  end
2959
2988
 
2960
2989
  describe "Dataset#to_csv" do
2961
2990
  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} ]
2991
+ @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
2992
  end
2980
2993
 
2981
2994
  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"
2995
+ @ds.to_csv.should == "a, b, c\r\n1, 2, 3\r\n4, 5, 6\r\n7, 8, 9\r\n"
2984
2996
  end
2985
2997
 
2986
2998
  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"
2999
+ @ds.to_csv(false).should == "1, 2, 3\r\n4, 5, 6\r\n7, 8, 9\r\n"
2989
3000
  end
2990
3001
  end
2991
3002
 
@@ -3007,7 +3018,7 @@ describe "Dataset#update_sql" do
3007
3018
  end
3008
3019
 
3009
3020
  specify "should accept array subscript references" do
3010
- @ds.update_sql((:day.sql_subscript(1)) => 'd').should == "UPDATE items SET day[1] = 'd'"
3021
+ @ds.update_sql((Sequel.subscript(:day, 1)) => 'd').should == "UPDATE items SET day[1] = 'd'"
3011
3022
  end
3012
3023
  end
3013
3024
 
@@ -3024,8 +3035,13 @@ describe "Dataset#insert_sql" do
3024
3035
  @ds.insert_sql('c' => 'd').should == "INSERT INTO items (c) VALUES ('d')"
3025
3036
  end
3026
3037
 
3038
+ specify "should quote string keys" do
3039
+ @ds.quote_identifiers = true
3040
+ @ds.insert_sql('c' => 'd').should == "INSERT INTO \"items\" (\"c\") VALUES ('d')"
3041
+ end
3042
+
3027
3043
  specify "should accept array subscript references" do
3028
- @ds.insert_sql((:day.sql_subscript(1)) => 'd').should == "INSERT INTO items (day[1]) VALUES ('d')"
3044
+ @ds.insert_sql((Sequel.subscript(:day, 1)) => 'd').should == "INSERT INTO items (day[1]) VALUES ('d')"
3029
3045
  end
3030
3046
 
3031
3047
  specify "should raise an Error if the dataset has no sources" do
@@ -3063,164 +3079,117 @@ describe "Dataset#insert_sql" do
3063
3079
  end
3064
3080
 
3065
3081
  specify "should accept a single LiteralString" do
3066
- @ds.insert_sql('VALUES (1, 2, 3)'.lit).should == "INSERT INTO items VALUES (1, 2, 3)"
3082
+ @ds.insert_sql(Sequel.lit('VALUES (1, 2, 3)')).should == "INSERT INTO items VALUES (1, 2, 3)"
3067
3083
  end
3068
3084
 
3069
3085
  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')"
3086
+ @ds.insert_sql([:a, :b, :c], Sequel.lit('VALUES (1, 2, 3)')).should == "INSERT INTO items (a, b, c) VALUES (1, 2, 3)"
3083
3087
  end
3084
3088
  end
3085
3089
 
3086
- class DummyMummyDataset < Sequel::Dataset
3087
- def first
3088
- raise if @opts[:from] == [:a]
3089
- true
3090
+ describe "Dataset#inspect" do
3091
+ before do
3092
+ class ::InspectDataset < Sequel::Dataset; end
3090
3093
  end
3091
- end
3092
-
3093
- class DummyMummyDatabase < Sequel::Database
3094
- attr_reader :sqls
3095
-
3096
- def execute(sql)
3097
- @sqls ||= []
3098
- @sqls << sql
3094
+ after do
3095
+ Object.send(:remove_const, :InspectDataset) if defined?(::InspectDataset)
3099
3096
  end
3100
-
3101
- def transaction; yield; end
3102
3097
 
3103
- def dataset
3104
- DummyMummyDataset.new(self)
3098
+ specify "should include the class name and the corresponding SQL statement" do
3099
+ Sequel::Dataset.new(nil).from(:blah).inspect.should == '#<Sequel::Dataset: "SELECT * FROM blah">'
3100
+ InspectDataset.new(nil).from(:blah).inspect.should == '#<InspectDataset: "SELECT * FROM blah">'
3105
3101
  end
3106
- end
3107
3102
 
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]
3103
+ specify "should skip anonymous classes" do
3104
+ Class.new(Class.new(Sequel::Dataset)).new(nil).from(:blah).inspect.should == '#<Sequel::Dataset: "SELECT * FROM blah">'
3105
+ Class.new(InspectDataset).new(nil).from(:blah).inspect.should == '#<InspectDataset: "SELECT * FROM blah">'
3115
3106
  end
3116
3107
  end
3117
3108
 
3118
3109
  describe "Dataset#all" do
3119
3110
  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)
3111
+ @dataset = Sequel.mock(:fetch=>[{:x => 1, :y => 2}, {:x => 3, :y => 4}])[:items]
3128
3112
  end
3129
3113
 
3130
3114
  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
- ]
3115
+ @dataset.all.should == [{:x => 1, :y => 2}, {:x => 3, :y => 4}]
3116
+ @dataset.db.sqls.should == ["SELECT * FROM items"]
3136
3117
  end
3137
3118
 
3138
3119
  specify "should iterate over the array if a block is given" do
3139
3120
  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"]
3121
+ @dataset.all{|r| a << r.values_at(:x, :y)}.should == [{:x => 1, :y => 2}, {:x => 3, :y => 4}]
3122
+ a.should == [[1, 2], [3, 4]]
3123
+ @dataset.db.sqls.should == ["SELECT * FROM items"]
3146
3124
  end
3147
3125
  end
3148
3126
 
3149
3127
  describe "Dataset#grep" do
3150
3128
  before do
3151
- @ds = Sequel::Dataset.new(nil).from(:posts)
3129
+ @ds = Sequel.mock[:posts]
3152
3130
  end
3153
3131
 
3154
3132
  specify "should format a SQL filter correctly" do
3155
- @ds.grep(:title, 'ruby').sql.should ==
3156
- "SELECT * FROM posts WHERE ((title LIKE 'ruby'))"
3133
+ @ds.grep(:title, 'ruby').sql.should == "SELECT * FROM posts WHERE ((title LIKE 'ruby'))"
3157
3134
  end
3158
3135
 
3159
3136
  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'))"
3137
+ @ds.grep([:title, :body], 'ruby').sql.should == "SELECT * FROM posts WHERE ((title LIKE 'ruby') OR (body LIKE 'ruby'))"
3162
3138
  end
3163
3139
 
3164
3140
  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'))"
3141
+ @ds.grep(:title, ['abc', 'def']).sql.should == "SELECT * FROM posts WHERE ((title LIKE 'abc') OR (title LIKE 'def'))"
3167
3142
  end
3168
3143
 
3169
3144
  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'))"
3145
+ @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
3146
  end
3173
3147
 
3174
3148
  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')))"
3149
+ @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
3150
  end
3178
3151
 
3179
3152
  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')))"
3153
+ @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
3154
  end
3183
3155
 
3184
3156
  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'))"
3157
+ @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
3158
  end
3188
3159
 
3189
3160
  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'))"
3161
+ @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
3162
  end
3193
3163
 
3194
3164
  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')))"
3165
+ @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
3166
  end
3198
3167
 
3199
3168
  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')))"
3169
+ @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
3170
  end
3203
3171
 
3204
3172
  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'))"
3173
+ @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
3174
  end
3208
3175
 
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'))"
3176
+ specify "should not support regexps if the database doesn't supports it" do
3177
+ proc{@ds.grep(:title, /ruby/).sql}.should raise_error(Sequel::InvalidOperation)
3178
+ proc{@ds.grep(:title, [/^ruby/, 'ruby']).sql}.should raise_error(Sequel::InvalidOperation)
3179
+ end
3212
3180
 
3213
- @ds.grep(:title, [/^ruby/, 'ruby']).sql.should ==
3214
- "SELECT * FROM posts WHERE ((title ~ '^ruby') OR (title LIKE 'ruby'))"
3181
+ specify "should support regexps if the database supports it" do
3182
+ def @ds.supports_regexp?; true end
3183
+ @ds.grep(:title, /ruby/).sql.should == "SELECT * FROM posts WHERE ((title ~ 'ruby'))"
3184
+ @ds.grep(:title, [/^ruby/, 'ruby']).sql.should == "SELECT * FROM posts WHERE ((title ~ '^ruby') OR (title LIKE 'ruby'))"
3215
3185
  end
3216
3186
 
3217
3187
  specify "should support searching against other columns" do
3218
- @ds.grep(:title, :body).sql.should ==
3219
- "SELECT * FROM posts WHERE ((title LIKE body))"
3188
+ @ds.grep(:title, :body).sql.should == "SELECT * FROM posts WHERE ((title LIKE body))"
3220
3189
  end
3221
3190
  end
3222
3191
 
3223
- describe "Dataset default #fetch_rows, #insert, #update, #delete, #truncate, #execute" do
3192
+ describe "Dataset default #fetch_rows, #insert, #update, #delete, #with_sql_delete, #truncate, #execute" do
3224
3193
  before do
3225
3194
  @db = Sequel::Database.new
3226
3195
  @ds = @db[:items]
@@ -3237,6 +3206,14 @@ describe "Dataset default #fetch_rows, #insert, #update, #delete, #truncate, #ex
3237
3206
  @ds.delete
3238
3207
  end
3239
3208
 
3209
+ specify "#with_sql_delete should execute delete SQL" do
3210
+ sql = 'DELETE FROM foo'
3211
+ @db.should_receive(:execute).once.with(sql, :server=>:default)
3212
+ @ds.with_sql_delete(sql)
3213
+ @db.should_receive(:execute_dui).once.with(sql, :server=>:default)
3214
+ @ds.with_sql_delete(sql)
3215
+ end
3216
+
3240
3217
  specify "#insert should execute insert SQL" do
3241
3218
  @db.should_receive(:execute).once.with('INSERT INTO items DEFAULT VALUES', :server=>:default)
3242
3219
  @ds.insert([])
@@ -3260,6 +3237,7 @@ describe "Dataset default #fetch_rows, #insert, #update, #delete, #truncate, #ex
3260
3237
 
3261
3238
  specify "#truncate should raise an InvalidOperation exception if the dataset is filtered" do
3262
3239
  proc{@ds.filter(:a=>1).truncate}.should raise_error(Sequel::InvalidOperation)
3240
+ proc{@ds.having(:a=>1).truncate}.should raise_error(Sequel::InvalidOperation)
3263
3241
  end
3264
3242
 
3265
3243
  specify "#execute should execute the SQL on the database" do
@@ -3270,58 +3248,80 @@ end
3270
3248
 
3271
3249
  describe "Dataset prepared statements and bound variables " do
3272
3250
  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
3251
+ @db = Sequel.mock
3285
3252
  @ds = @db[:items]
3253
+ @ds.meta_def(:insert_sql){|*v| "#{super(*v)}#{' RETURNING *' if opts.has_key?(:returning)}" }
3286
3254
  end
3287
3255
 
3288
3256
  specify "#call should take a type and bind hash and interpolate it" do
3257
+ @ds.filter(:num=>:$n).call(:each, :n=>1)
3289
3258
  @ds.filter(:num=>:$n).call(:select, :n=>1)
3259
+ @ds.filter(:num=>:$n).call([:map, :a], :n=>1)
3260
+ @ds.filter(:num=>:$n).call([:to_hash, :a, :b], :n=>1)
3261
+ @ds.filter(:num=>:$n).call([:to_hash_groups, :a, :b], :n=>1)
3290
3262
  @ds.filter(:num=>:$n).call(:first, :n=>1)
3291
3263
  @ds.filter(:num=>:$n).call(:delete, :n=>1)
3292
3264
  @ds.filter(:num=>:$n).call(:update, {:n=>1, :n2=>2}, :num=>:$n2)
3293
3265
  @ds.call(:insert, {:n=>1}, :num=>:$n)
3294
- @db.sqls.should == ['SELECT * FROM items WHERE (num = 1)',
3266
+ @ds.call(:insert_select, {:n=>1}, :num=>:$n)
3267
+ @db.sqls.should == [
3268
+ 'SELECT * FROM items WHERE (num = 1)',
3269
+ 'SELECT * FROM items WHERE (num = 1)',
3270
+ 'SELECT * FROM items WHERE (num = 1)',
3271
+ 'SELECT * FROM items WHERE (num = 1)',
3272
+ 'SELECT * FROM items WHERE (num = 1)',
3295
3273
  'SELECT * FROM items WHERE (num = 1) LIMIT 1',
3296
3274
  'DELETE FROM items WHERE (num = 1)',
3297
3275
  'UPDATE items SET num = 2 WHERE (num = 1)',
3298
- 'INSERT INTO items (num) VALUES (1)']
3276
+ 'INSERT INTO items (num) VALUES (1)',
3277
+ 'INSERT INTO items (num) VALUES (1) RETURNING *']
3299
3278
  end
3300
3279
 
3301
3280
  specify "#prepare should take a type and name and store it in the database for later use with call" do
3302
3281
  pss = []
3282
+ pss << @ds.filter(:num=>:$n).prepare(:each, :en)
3303
3283
  pss << @ds.filter(:num=>:$n).prepare(:select, :sn)
3284
+ pss << @ds.filter(:num=>:$n).prepare([:map, :a], :sm)
3285
+ pss << @ds.filter(:num=>:$n).prepare([:to_hash, :a, :b], :sh)
3286
+ pss << @ds.filter(:num=>:$n).prepare([:to_hash_groups, :a, :b], :shg)
3304
3287
  pss << @ds.filter(:num=>:$n).prepare(:first, :fn)
3305
3288
  pss << @ds.filter(:num=>:$n).prepare(:delete, :dn)
3306
3289
  pss << @ds.filter(:num=>:$n).prepare(:update, :un, :num=>:$n2)
3307
3290
  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]}
3291
+ pss << @ds.prepare(:insert_select, :ins, :num=>:$n)
3292
+ @db.prepared_statements.keys.sort_by{|k| k.to_s}.should == [:dn, :en, :fn, :in, :ins, :sh, :shg, :sm, :sn, :un]
3293
+ [:en, :sn, :sm, :sh, :shg, :fn, :dn, :un, :in, :ins].each_with_index{|x, i| @db.prepared_statements[x].should == pss[i]}
3294
+ @db.call(:en, :n=>1){}
3310
3295
  @db.call(:sn, :n=>1)
3296
+ @db.call(:sm, :n=>1)
3297
+ @db.call(:sh, :n=>1)
3298
+ @db.call(:shg, :n=>1)
3311
3299
  @db.call(:fn, :n=>1)
3312
3300
  @db.call(:dn, :n=>1)
3313
3301
  @db.call(:un, :n=>1, :n2=>2)
3314
3302
  @db.call(:in, :n=>1)
3315
- @db.sqls.should == ['SELECT * FROM items WHERE (num = 1)',
3303
+ @db.call(:ins, :n=>1)
3304
+ @db.sqls.should == [
3305
+ 'SELECT * FROM items WHERE (num = 1)',
3306
+ 'SELECT * FROM items WHERE (num = 1)',
3307
+ 'SELECT * FROM items WHERE (num = 1)',
3308
+ 'SELECT * FROM items WHERE (num = 1)',
3309
+ 'SELECT * FROM items WHERE (num = 1)',
3316
3310
  'SELECT * FROM items WHERE (num = 1) LIMIT 1',
3317
3311
  'DELETE FROM items WHERE (num = 1)',
3318
3312
  'UPDATE items SET num = 2 WHERE (num = 1)',
3319
- 'INSERT INTO items (num) VALUES (1)']
3313
+ 'INSERT INTO items (num) VALUES (1)',
3314
+ 'INSERT INTO items (num) VALUES (1) RETURNING *']
3320
3315
  end
3321
3316
 
3317
+ specify "#call should default to using :all if an invalid type is given" do
3318
+ @ds.filter(:num=>:$n).call(:select_all, :n=>1)
3319
+ @db.sqls.should == ['SELECT * FROM items WHERE (num = 1)']
3320
+ end
3321
+
3322
3322
  specify "#inspect should indicate it is a prepared statement with the prepared SQL" do
3323
3323
  @ds.filter(:num=>:$n).prepare(:select, :sn).inspect.should == \
3324
- '<Sequel::Dataset/PreparedStatement "SELECT * FROM items WHERE (num = $n)">'
3324
+ '<Sequel::Mock::Dataset/PreparedStatement "SELECT * FROM items WHERE (num = $n)">'
3325
3325
  end
3326
3326
 
3327
3327
  specify "should handle literal strings" do
@@ -3329,6 +3329,13 @@ describe "Dataset prepared statements and bound variables " do
3329
3329
  @db.sqls.should == ['SELECT * FROM items WHERE (num = 1)']
3330
3330
  end
3331
3331
 
3332
+ specify "should handle columns on prepared statements correctly" do
3333
+ @db.columns = [:num]
3334
+ @ds.meta_def(:select_where_sql){|sql| super(sql); sql << " OR #{columns.first} = 1" if opts[:where]}
3335
+ @ds.filter(:num=>:$n).prepare(:select, :sn).sql.should == 'SELECT * FROM items WHERE (num = $n) OR num = 1'
3336
+ @db.sqls.should == ['SELECT * FROM items LIMIT 1']
3337
+ end
3338
+
3332
3339
  specify "should handle datasets using static sql and placeholders" do
3333
3340
  @db["SELECT * FROM items WHERE (num = ?)", :$n].call(:select, :n=>1)
3334
3341
  @db.sqls.should == ['SELECT * FROM items WHERE (num = 1)']
@@ -3357,23 +3364,16 @@ end
3357
3364
 
3358
3365
  describe Sequel::Dataset::UnnumberedArgumentMapper do
3359
3366
  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
3367
+ @db = Sequel.mock
3365
3368
  @ds = @db[:items].filter(:num=>:$n)
3366
- def @ds.fetch_rows(sql, &block)
3367
- execute(sql)
3368
- end
3369
3369
  def @ds.execute(sql, opts={}, &block)
3370
- @db.execute(sql, {:arguments=>bind_arguments}.merge(opts))
3370
+ super(sql, opts.merge({:arguments=>bind_arguments}), &block)
3371
3371
  end
3372
- def @ds.execute_dui(*args, &block)
3373
- execute(*args, &block)
3372
+ def @ds.execute_dui(sql, opts={}, &block)
3373
+ super(sql, opts.merge({:arguments=>bind_arguments}), &block)
3374
3374
  end
3375
- def @ds.execute_insert(*args, &block)
3376
- execute(*args, &block)
3375
+ def @ds.execute_insert(sql, opts={}, &block)
3376
+ super(sql, opts.merge({:arguments=>bind_arguments}), &block)
3377
3377
  end
3378
3378
  @ps = []
3379
3379
  @ps << @ds.prepare(:select, :s)
@@ -3386,64 +3386,51 @@ describe Sequel::Dataset::UnnumberedArgumentMapper do
3386
3386
  end
3387
3387
 
3388
3388
  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 = ?)">'
3389
+ @ps.first.inspect.should == '<Sequel::Mock::Dataset/PreparedStatement "SELECT * FROM items WHERE (num = ?)">'
3390
3390
  end
3391
3391
 
3392
- specify "should submitted the SQL to the database with placeholders and bind variables" do
3392
+ specify "should submit the SQL to the database with placeholders and bind variables" do
3393
3393
  @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
- ]
3394
+ @db.sqls.should == ["SELECT * FROM items WHERE (num = ?) -- args: [1]",
3395
+ "SELECT * FROM items WHERE (num = ?) -- args: [1]",
3396
+ "SELECT * FROM items WHERE (num = ?) LIMIT 1 -- args: [1]",
3397
+ "DELETE FROM items WHERE (num = ?) -- args: [1]",
3398
+ "INSERT INTO items (num) VALUES (?) -- args: [1]",
3399
+ "UPDATE items SET num = ? WHERE (num = ?) -- args: [1, 1]"]
3400
+ end
3401
+
3402
+ specify "should handle unrecognized statement types as :all" do
3403
+ ps = @ds.prepare(:select_all, :s)
3404
+ ps.extend(Sequel::Dataset::UnnumberedArgumentMapper)
3405
+ ps.prepared_sql
3406
+ ps.call(:n=>1)
3407
+ @db.sqls.should == ["SELECT * FROM items WHERE (num = ?) -- args: [1]"]
3401
3408
  end
3402
3409
  end
3403
3410
 
3404
3411
  describe "Sequel::Dataset#server" do
3405
3412
  specify "should set the server to use for the dataset" do
3406
- @db = Sequel::Database.new
3413
+ @db = Sequel.mock(:servers=>{:s=>{}, :i=>{}, :d=>{}, :u=>{}})
3407
3414
  @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
3415
  @ds.all
3416
3416
  @ds.server(:i).insert(:a=>1)
3417
3417
  @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]]
3418
+ @ds.server(:u).update(:a=>Sequel.expr(:a)+1)
3419
+ @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
3420
  end
3424
3421
  end
3425
3422
 
3426
3423
  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
3424
  specify "should yield a dataset for each server" do
3425
+ @db = Sequel.mock(:servers=>{:s=>{}, :i=>{}})
3426
+ @ds = @db[:items]
3440
3427
  @ds.each_server do |ds|
3441
3428
  ds.should be_a_kind_of(Sequel::Dataset)
3442
3429
  ds.should_not == @ds
3443
3430
  ds.sql.should == @ds.sql
3444
3431
  ds.all
3445
3432
  end
3446
- @sqls.sort_by{|sql, s| s.to_s}.should == [['SELECT * FROM items', :default], ['SELECT * FROM items', :i], ['SELECT * FROM items', :s]]
3433
+ @db.sqls.sort.should == ['SELECT * FROM items', 'SELECT * FROM items -- i', 'SELECT * FROM items -- s']
3447
3434
  end
3448
3435
  end
3449
3436
 
@@ -3493,23 +3480,23 @@ end
3493
3480
 
3494
3481
  describe "Sequel::Dataset#qualify" do
3495
3482
  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)'
3483
+ Sequel::Dataset.new(nil).from(:t).filter{a<b}.qualify(:e).sql.should == 'SELECT e.* FROM t WHERE (e.a < e.b)'
3497
3484
  end
3498
3485
 
3499
3486
  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)'
3487
+ Sequel::Dataset.new(nil).from(:t).filter{a<b}.qualify.sql.should == 'SELECT t.* FROM t WHERE (t.a < t.b)'
3501
3488
  end
3502
3489
  end
3503
3490
 
3504
3491
  describe "Sequel::Dataset#qualify_to" do
3505
3492
  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)'
3493
+ Sequel::Dataset.new(nil).from(:t).filter{a<b}.qualify_to(:e).sql.should == 'SELECT e.* FROM t WHERE (e.a < e.b)'
3507
3494
  end
3508
3495
  end
3509
3496
 
3510
3497
  describe "Sequel::Dataset#qualify_to_first_source" do
3511
3498
  before do
3512
- @ds = MockDatabase.new[:t]
3499
+ @ds = Sequel::Database.new[:t]
3513
3500
  end
3514
3501
 
3515
3502
  specify "should qualify_to the first source" do
@@ -3519,13 +3506,11 @@ describe "Sequel::Dataset#qualify_to_first_source" do
3519
3506
  end
3520
3507
 
3521
3508
  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'
3509
+ @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
3510
  end
3525
3511
 
3526
3512
  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'
3513
+ @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
3514
  end
3530
3515
 
3531
3516
  specify "should handle hashes in select option" do
@@ -3541,7 +3526,7 @@ describe "Sequel::Dataset#qualify_to_first_source" do
3541
3526
  end
3542
3527
 
3543
3528
  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"
3529
+ @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
3530
  end
3546
3531
 
3547
3532
  specify "should handle SQL::Identifiers" do
@@ -3549,15 +3534,15 @@ describe "Sequel::Dataset#qualify_to_first_source" do
3549
3534
  end
3550
3535
 
3551
3536
  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'
3537
+ @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
3538
  end
3554
3539
 
3555
3540
  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'
3541
+ @ds.select(Sequel.expr(:a).as(:b)).qualify_to_first_source.sql.should == 'SELECT t.a AS b FROM t'
3557
3542
  end
3558
3543
 
3559
3544
  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)'
3545
+ @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
3546
  end
3562
3547
 
3563
3548
  specify "should handle SQL:Casts" do
@@ -3573,7 +3558,7 @@ describe "Sequel::Dataset#qualify_to_first_source" do
3573
3558
  end
3574
3559
 
3575
3560
  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))'
3561
+ @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
3562
  end
3578
3563
 
3579
3564
  specify "should handle SQL::Subscripts" do
@@ -3588,20 +3573,97 @@ describe "Sequel::Dataset#qualify_to_first_source" do
3588
3573
  @ds.filter(':a > :b', :a=>:c, :b=>1).qualify_to_first_source.sql.should == 'SELECT t.* FROM t WHERE (t.c > 1)'
3589
3574
  end
3590
3575
 
3576
+ specify "should handle SQL::Wrappers" do
3577
+ @ds.filter(Sequel::SQL::Wrapper.new(:a)).qualify_to_first_source.sql.should == 'SELECT t.* FROM t WHERE t.a'
3578
+ end
3579
+
3591
3580
  specify "should handle SQL::WindowFunctions" do
3592
3581
  @ds.meta_def(:supports_window_functions?){true}
3593
3582
  @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
3583
  end
3595
3584
 
3596
3585
  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"
3586
+ @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"
3587
+ end
3588
+ end
3589
+
3590
+ describe "Sequel::Dataset#unbind" do
3591
+ before do
3592
+ @ds = Sequel::Database.new[:t]
3593
+ @u = proc{|ds| ds, bv = ds.unbind; [ds.sql, bv]}
3594
+ end
3595
+
3596
+ specify "should unbind values assigned to equality and inequality statements" do
3597
+ @ds.filter(:foo=>1).unbind.first.sql.should == "SELECT * FROM t WHERE (foo = $foo)"
3598
+ @ds.exclude(:foo=>1).unbind.first.sql.should == "SELECT * FROM t WHERE (foo != $foo)"
3599
+ @ds.filter{foo > 1}.unbind.first.sql.should == "SELECT * FROM t WHERE (foo > $foo)"
3600
+ @ds.filter{foo >= 1}.unbind.first.sql.should == "SELECT * FROM t WHERE (foo >= $foo)"
3601
+ @ds.filter{foo < 1}.unbind.first.sql.should == "SELECT * FROM t WHERE (foo < $foo)"
3602
+ @ds.filter{foo <= 1}.unbind.first.sql.should == "SELECT * FROM t WHERE (foo <= $foo)"
3603
+ end
3604
+
3605
+ specify "should return variables that could be used bound to recreate the previous query" do
3606
+ @ds.filter(:foo=>1).unbind.last.should == {:foo=>1}
3607
+ @ds.exclude(:foo=>1).unbind.last.should == {:foo=>1}
3608
+ end
3609
+
3610
+ specify "should handle numerics, strings, dates, times, and datetimes" do
3611
+ @u[@ds.filter(:foo=>1)].should == ["SELECT * FROM t WHERE (foo = $foo)", {:foo=>1}]
3612
+ @u[@ds.filter(:foo=>1.0)].should == ["SELECT * FROM t WHERE (foo = $foo)", {:foo=>1.0}]
3613
+ @u[@ds.filter(:foo=>BigDecimal.new('1.0'))].should == ["SELECT * FROM t WHERE (foo = $foo)", {:foo=>BigDecimal.new('1.0')}]
3614
+ @u[@ds.filter(:foo=>'a')].should == ["SELECT * FROM t WHERE (foo = $foo)", {:foo=>'a'}]
3615
+ @u[@ds.filter(:foo=>Date.today)].should == ["SELECT * FROM t WHERE (foo = $foo)", {:foo=>Date.today}]
3616
+ t = Time.now
3617
+ @u[@ds.filter(:foo=>t)].should == ["SELECT * FROM t WHERE (foo = $foo)", {:foo=>t}]
3618
+ dt = DateTime.now
3619
+ @u[@ds.filter(:foo=>dt)].should == ["SELECT * FROM t WHERE (foo = $foo)", {:foo=>dt}]
3620
+ end
3621
+
3622
+ specify "should not unbind literal strings" do
3623
+ @u[@ds.filter(:foo=>Sequel.lit('a'))].should == ["SELECT * FROM t WHERE (foo = a)", {}]
3624
+ end
3625
+
3626
+ specify "should not unbind Identifiers, QualifiedIdentifiers, or Symbols used as booleans" do
3627
+ @u[@ds.filter(:foo).filter{bar}.filter{foo__bar}].should == ["SELECT * FROM t WHERE (foo AND bar AND foo.bar)", {}]
3628
+ end
3629
+
3630
+ specify "should not unbind for values it doesn't understand" do
3631
+ @u[@ds.filter(:foo=>Class.new{def sql_literal(ds) 'bar' end}.new)].should == ["SELECT * FROM t WHERE (foo = bar)", {}]
3632
+ end
3633
+
3634
+ specify "should handle QualifiedIdentifiers" do
3635
+ @u[@ds.filter{foo__bar > 1}].should == ["SELECT * FROM t WHERE (foo.bar > $foo.bar)", {:"foo.bar"=>1}]
3636
+ end
3637
+
3638
+ specify "should handle wrapped objects" do
3639
+ @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}]
3640
+ end
3641
+
3642
+ specify "should handle deep nesting" do
3643
+ @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}]
3644
+ end
3645
+
3646
+ specify "should handle JOIN ON" do
3647
+ @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}]
3648
+ end
3649
+
3650
+ specify "should raise an UnbindDuplicate exception if same variable is used with multiple different values" do
3651
+ proc{@ds.filter(:foo=>1).or(:foo=>2).unbind}.should raise_error(Sequel::UnbindDuplicate)
3652
+ end
3653
+
3654
+ specify "should handle case where the same variable has the same value in multiple places " do
3655
+ @u[@ds.filter(:foo=>1).or(:foo=>1)].should == ["SELECT * FROM t WHERE ((foo = $foo) OR (foo = $foo))", {:foo=>1}]
3656
+ end
3657
+
3658
+ specify "should raise Error for unhandled objects inside Identifiers and QualifiedIndentifiers" do
3659
+ proc{@ds.filter(Sequel::SQL::Identifier.new([]) > 1).unbind}.should raise_error(Sequel::Error)
3660
+ proc{@ds.filter{foo.qualify({}) > 1}.unbind}.should raise_error(Sequel::Error)
3599
3661
  end
3600
3662
  end
3601
3663
 
3602
3664
  describe "Sequel::Dataset #with and #with_recursive" do
3603
3665
  before do
3604
- @db = MockDatabase.new
3666
+ @db = Sequel::Database.new
3605
3667
  @ds = @db[:t]
3606
3668
  end
3607
3669
 
@@ -3624,6 +3686,12 @@ describe "Sequel::Dataset #with and #with_recursive" do
3624
3686
  @ds.with_recursive(:t, @db[:x], @db[:t], :args=>[:b, :c]).sql.should == 'WITH t(b, c) AS (SELECT * FROM x UNION ALL SELECT * FROM t) SELECT * FROM t'
3625
3687
  end
3626
3688
 
3689
+ specify "#with and #with_recursive should quote the columns in the :args option" do
3690
+ @ds.quote_identifiers = true
3691
+ @ds.with(:t, @db[:x], :args=>[:b]).sql.should == 'WITH "t"("b") AS (SELECT * FROM x) SELECT * FROM "t"'
3692
+ @ds.with_recursive(:t, @db[:x], @db[:t], :args=>[:b, :c]).sql.should == 'WITH "t"("b", "c") AS (SELECT * FROM x UNION ALL SELECT * FROM t) SELECT * FROM "t"'
3693
+ end
3694
+
3627
3695
  specify "#with_recursive should take an :union_all=>false option" do
3628
3696
  @ds.with_recursive(:t, @db[:x], @db[:t], :union_all=>false).sql.should == 'WITH t AS (SELECT * FROM x UNION SELECT * FROM t) SELECT * FROM t'
3629
3697
  end
@@ -3633,56 +3701,72 @@ describe "Sequel::Dataset #with and #with_recursive" do
3633
3701
  proc{@ds.with(:t, @db[:x], :args=>[:b])}.should raise_error(Sequel::Error)
3634
3702
  proc{@ds.with_recursive(:t, @db[:x], @db[:t], :args=>[:b, :c])}.should raise_error(Sequel::Error)
3635
3703
  end
3704
+
3705
+ specify "#with should work on insert, update, and delete statements if they support it" do
3706
+ [:insert, :update, :delete].each do |m|
3707
+ @ds.meta_def(:"#{m}_clause_methods"){[:"#{m}_with_sql"] + super()}
3708
+ end
3709
+ @ds.with(:t, @db[:x]).insert_sql(1).should == 'WITH t AS (SELECT * FROM x) INSERT INTO t VALUES (1)'
3710
+ @ds.with(:t, @db[:x]).update_sql(:foo=>1).should == 'WITH t AS (SELECT * FROM x) UPDATE t SET foo = 1'
3711
+ @ds.with(:t, @db[:x]).delete_sql.should == 'WITH t AS (SELECT * FROM x) DELETE FROM t'
3712
+ end
3713
+
3714
+ specify "should hoist WITH clauses in given dataset(s) if dataset doesn't support WITH in subselect" do
3715
+ @ds.meta_def(:supports_cte?){true}
3716
+ @ds.meta_def(:supports_cte_in_subselect?){false}
3717
+ @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'
3718
+ @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'
3719
+ end
3636
3720
  end
3637
3721
 
3638
3722
  describe Sequel::SQL::Constants do
3639
3723
  before do
3640
- @db = MockDatabase.new
3724
+ @db = Sequel::Database.new
3641
3725
  end
3642
3726
 
3643
3727
  it "should have CURRENT_DATE" do
3644
- @db.literal(Sequel::SQL::Constants::CURRENT_DATE) == 'CURRENT_DATE'
3645
- @db.literal(Sequel::CURRENT_DATE) == 'CURRENT_DATE'
3728
+ @db.literal(Sequel::SQL::Constants::CURRENT_DATE).should == 'CURRENT_DATE'
3729
+ @db.literal(Sequel::CURRENT_DATE).should == 'CURRENT_DATE'
3646
3730
  end
3647
3731
 
3648
3732
  it "should have CURRENT_TIME" do
3649
- @db.literal(Sequel::SQL::Constants::CURRENT_TIME) == 'CURRENT_TIME'
3650
- @db.literal(Sequel::CURRENT_TIME) == 'CURRENT_TIME'
3733
+ @db.literal(Sequel::SQL::Constants::CURRENT_TIME).should == 'CURRENT_TIME'
3734
+ @db.literal(Sequel::CURRENT_TIME).should == 'CURRENT_TIME'
3651
3735
  end
3652
3736
 
3653
3737
  it "should have CURRENT_TIMESTAMP" do
3654
- @db.literal(Sequel::SQL::Constants::CURRENT_TIMESTAMP) == 'CURRENT_TIMESTAMP'
3655
- @db.literal(Sequel::CURRENT_TIMESTAMP) == 'CURRENT_TIMESTAMP'
3738
+ @db.literal(Sequel::SQL::Constants::CURRENT_TIMESTAMP).should == 'CURRENT_TIMESTAMP'
3739
+ @db.literal(Sequel::CURRENT_TIMESTAMP).should == 'CURRENT_TIMESTAMP'
3656
3740
  end
3657
3741
 
3658
3742
  it "should have NULL" do
3659
- @db.literal(Sequel::SQL::Constants::NULL) == 'NULL'
3660
- @db.literal(Sequel::NULL) == 'NULL'
3743
+ @db.literal(Sequel::SQL::Constants::NULL).should == 'NULL'
3744
+ @db.literal(Sequel::NULL).should == 'NULL'
3661
3745
  end
3662
3746
 
3663
3747
  it "should have NOTNULL" do
3664
- @db.literal(Sequel::SQL::Constants::NOTNULL) == 'NOT NULL'
3665
- @db.literal(Sequel::NOTNULL) == 'NOT NULL'
3748
+ @db.literal(Sequel::SQL::Constants::NOTNULL).should == 'NOT NULL'
3749
+ @db.literal(Sequel::NOTNULL).should == 'NOT NULL'
3666
3750
  end
3667
3751
 
3668
3752
  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'
3753
+ @db.literal(Sequel::SQL::Constants::TRUE).should == "'t'"
3754
+ @db.literal(Sequel::TRUE).should == "'t'"
3755
+ @db.literal(Sequel::SQL::Constants::SQLTRUE).should == "'t'"
3756
+ @db.literal(Sequel::SQLTRUE).should == "'t'"
3673
3757
  end
3674
3758
 
3675
3759
  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'
3760
+ @db.literal(Sequel::SQL::Constants::FALSE).should == "'f'"
3761
+ @db.literal(Sequel::FALSE).should == "'f'"
3762
+ @db.literal(Sequel::SQL::Constants::SQLFALSE).should == "'f'"
3763
+ @db.literal(Sequel::SQLFALSE).should == "'f'"
3680
3764
  end
3681
3765
  end
3682
3766
 
3683
3767
  describe "Sequel timezone support" do
3684
3768
  before do
3685
- @db = MockDatabase.new
3769
+ @db = Sequel::Database.new
3686
3770
  @dataset = @db.dataset
3687
3771
  @dataset.meta_def(:supports_timestamp_timezones?){true}
3688
3772
  @dataset.meta_def(:supports_timestamp_usecs?){false}
@@ -3717,6 +3801,30 @@ describe "Sequel timezone support" do
3717
3801
  @dataset.literal(t).should == "#{s}#{@offset}'"
3718
3802
  end
3719
3803
 
3804
+ specify "should have Database#timezone override Sequel.database_timezone" do
3805
+ Sequel.database_timezone = :local
3806
+ @db.timezone = :utc
3807
+
3808
+ t = Time.now
3809
+ s = t.getutc.strftime("'%Y-%m-%d %H:%M:%S")
3810
+ @dataset.literal(t).should == "#{s}+0000'"
3811
+
3812
+ t = DateTime.now
3813
+ s = t.new_offset(0).strftime("'%Y-%m-%d %H:%M:%S")
3814
+ @dataset.literal(t).should == "#{s}+0000'"
3815
+
3816
+ Sequel.database_timezone = :utc
3817
+ @db.timezone = :local
3818
+
3819
+ t = Time.now.utc
3820
+ s = t.getlocal.strftime("'%Y-%m-%d %H:%M:%S")
3821
+ @dataset.literal(t).should == "#{s}#{@offset}'"
3822
+
3823
+ t = DateTime.now.new_offset(0)
3824
+ s = t.new_offset(DateTime.now.offset).strftime("'%Y-%m-%d %H:%M:%S")
3825
+ @dataset.literal(t).should == "#{s}#{@offset}'"
3826
+ end
3827
+
3720
3828
  specify "should handle converting database timestamps into application timestamps" do
3721
3829
  Sequel.database_timezone = :utc
3722
3830
  Sequel.application_timezone = :local
@@ -3827,13 +3935,7 @@ end
3827
3935
 
3828
3936
  describe "Sequel::Dataset#select_map" do
3829
3937
  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
3938
+ @ds = Sequel.mock(:fetch=>[{:c=>1}, {:c=>2}])[:t]
3837
3939
  end
3838
3940
 
3839
3941
  specify "should do select and map in one step" do
@@ -3846,31 +3948,63 @@ describe "Sequel::Dataset#select_map" do
3846
3948
  @ds.db.sqls.should == ['SELECT a.b FROM t']
3847
3949
  end
3848
3950
 
3951
+ specify "should raise if multiple arguments and can't determine alias" do
3952
+ proc{@ds.select_map([Sequel.function(:a), :b])}.should raise_error(Sequel::Error)
3953
+ proc{@ds.select_map(Sequel.function(:a)){b}}.should raise_error(Sequel::Error)
3954
+ proc{@ds.select_map{[a{}, b]}}.should raise_error(Sequel::Error)
3955
+ end
3956
+
3849
3957
  specify "should handle implicit aliases in arguments" do
3850
3958
  @ds.select_map(:a___b).should == [1, 2]
3851
3959
  @ds.db.sqls.should == ['SELECT a AS b FROM t']
3852
3960
  end
3853
3961
 
3854
3962
  specify "should handle other objects" do
3855
- @ds.select_map("a".lit.as(:b)).should == [1, 2]
3963
+ @ds.select_map(Sequel.lit("a").as(:b)).should == [1, 2]
3856
3964
  @ds.db.sqls.should == ['SELECT a AS b FROM t']
3857
3965
  end
3858
3966
 
3967
+ specify "should handle identifiers with strings" do
3968
+ @ds.select_map([Sequel::SQL::Identifier.new('c'), :c]).should == [[1, 1], [2, 2]]
3969
+ @ds.db.sqls.should == ['SELECT c, c FROM t']
3970
+ end
3971
+
3859
3972
  specify "should accept a block" do
3860
3973
  @ds.select_map{a(t__c)}.should == [1, 2]
3861
3974
  @ds.db.sqls.should == ['SELECT a(t.c) FROM t']
3862
3975
  end
3976
+
3977
+ specify "should accept a block with an array of columns" do
3978
+ @ds.select_map{[a(t__c).as(c), a(t__c).as(c)]}.should == [[1, 1], [2, 2]]
3979
+ @ds.db.sqls.should == ['SELECT a(t.c) AS c, a(t.c) AS c FROM t']
3980
+ end
3981
+
3982
+ specify "should accept a block with a column" do
3983
+ @ds.select_map(:c){a(t__c).as(c)}.should == [[1, 1], [2, 2]]
3984
+ @ds.db.sqls.should == ['SELECT c, a(t.c) AS c FROM t']
3985
+ end
3986
+
3987
+ specify "should accept a block and array of arguments" do
3988
+ @ds.select_map([:c, :c]){[a(t__c).as(c), a(t__c).as(c)]}.should == [[1, 1, 1, 1], [2, 2, 2, 2]]
3989
+ @ds.db.sqls.should == ['SELECT c, c, a(t.c) AS c, a(t.c) AS c FROM t']
3990
+ end
3991
+
3992
+ specify "should handle an array of columns" do
3993
+ @ds.select_map([:c, :c]).should == [[1, 1], [2, 2]]
3994
+ @ds.db.sqls.should == ['SELECT c, c FROM t']
3995
+ @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]]
3996
+ @ds.db.sqls.should == ['SELECT d AS c, b.c, c, b.c, a.c, a.d AS c FROM t']
3997
+ end
3998
+
3999
+ specify "should handle an array with a single element" do
4000
+ @ds.select_map([:c]).should == [[1], [2]]
4001
+ @ds.db.sqls.should == ['SELECT c FROM t']
4002
+ end
3863
4003
  end
3864
4004
 
3865
4005
  describe "Sequel::Dataset#select_order_map" do
3866
4006
  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
4007
+ @ds = Sequel.mock(:fetch=>[{:c=>1}, {:c=>2}])[:t]
3874
4008
  end
3875
4009
 
3876
4010
  specify "should do select and map in one step" do
@@ -3883,6 +4017,12 @@ describe "Sequel::Dataset#select_order_map" do
3883
4017
  @ds.db.sqls.should == ['SELECT a.b FROM t ORDER BY a.b']
3884
4018
  end
3885
4019
 
4020
+ specify "should raise if multiple arguments and can't determine alias" do
4021
+ proc{@ds.select_order_map([Sequel.function(:a), :b])}.should raise_error(Sequel::Error)
4022
+ proc{@ds.select_order_map(Sequel.function(:a)){b}}.should raise_error(Sequel::Error)
4023
+ proc{@ds.select_order_map{[a{}, b]}}.should raise_error(Sequel::Error)
4024
+ end
4025
+
3886
4026
  specify "should handle implicit aliases in arguments" do
3887
4027
  @ds.select_order_map(:a___b).should == [1, 2]
3888
4028
  @ds.db.sqls.should == ['SELECT a AS b FROM t ORDER BY a']
@@ -3894,59 +4034,164 @@ describe "Sequel::Dataset#select_order_map" do
3894
4034
  end
3895
4035
 
3896
4036
  specify "should handle AliasedExpressions" do
3897
- @ds.select_order_map("a".lit.as(:b)).should == [1, 2]
4037
+ @ds.select_order_map(Sequel.lit("a").as(:b)).should == [1, 2]
3898
4038
  @ds.db.sqls.should == ['SELECT a AS b FROM t ORDER BY a']
3899
4039
  end
3900
4040
 
4041
+ specify "should handle OrderedExpressions" do
4042
+ @ds.select_order_map(Sequel.desc(:a)).should == [1, 2]
4043
+ @ds.db.sqls.should == ['SELECT a FROM t ORDER BY a DESC']
4044
+ end
4045
+
3901
4046
  specify "should accept a block" do
3902
4047
  @ds.select_order_map{a(t__c)}.should == [1, 2]
3903
4048
  @ds.db.sqls.should == ['SELECT a(t.c) FROM t ORDER BY a(t.c)']
3904
4049
  end
4050
+
4051
+ specify "should accept a block with an array of columns" do
4052
+ @ds.select_order_map{[c.desc, a(t__c).as(c)]}.should == [[1, 1], [2, 2]]
4053
+ @ds.db.sqls.should == ['SELECT c, a(t.c) AS c FROM t ORDER BY c DESC, a(t.c)']
4054
+ end
4055
+
4056
+ specify "should accept a block with a column" do
4057
+ @ds.select_order_map(:c){a(t__c).as(c)}.should == [[1, 1], [2, 2]]
4058
+ @ds.db.sqls.should == ['SELECT c, a(t.c) AS c FROM t ORDER BY c, a(t.c)']
4059
+ end
4060
+
4061
+ specify "should accept a block and array of arguments" do
4062
+ @ds.select_order_map([:c, :c]){[a(t__c).as(c), c.desc]}.should == [[1, 1, 1, 1], [2, 2, 2, 2]]
4063
+ @ds.db.sqls.should == ['SELECT c, c, a(t.c) AS c, c FROM t ORDER BY c, c, a(t.c), c DESC']
4064
+ end
4065
+
4066
+ specify "should handle an array of columns" do
4067
+ @ds.select_order_map([:c, :c]).should == [[1, 1], [2, 2]]
4068
+ @ds.db.sqls.should == ['SELECT c, c FROM t ORDER BY c, c']
4069
+ @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]]
4070
+ @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']
4071
+ end
4072
+
4073
+ specify "should handle an array with a single element" do
4074
+ @ds.select_order_map([:c]).should == [[1], [2]]
4075
+ @ds.db.sqls.should == ['SELECT c FROM t ORDER BY c']
4076
+ end
3905
4077
  end
3906
4078
 
3907
4079
  describe "Sequel::Dataset#select_hash" do
3908
4080
  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
4081
+ @db = Sequel.mock(:fetch=>[{:a=>1, :b=>2}, {:a=>3, :b=>4}])
4082
+ @ds = @db[:t]
3918
4083
  end
3919
4084
 
3920
- specify "should do select and map in one step" do
3921
- @ds.set_fr_yield([{:a=>1, :b=>2}, {:a=>3, :b=>4}])
4085
+ specify "should do select and to_hash in one step" do
3922
4086
  @ds.select_hash(:a, :b).should == {1=>2, 3=>4}
3923
4087
  @ds.db.sqls.should == ['SELECT a, b FROM t']
3924
4088
  end
3925
4089
 
3926
4090
  specify "should handle implicit qualifiers in arguments" do
3927
- @ds.set_fr_yield([{:a=>1, :b=>2}, {:a=>3, :b=>4}])
3928
4091
  @ds.select_hash(:t__a, :t__b).should == {1=>2, 3=>4}
3929
4092
  @ds.db.sqls.should == ['SELECT t.a, t.b FROM t']
3930
4093
  end
3931
4094
 
3932
4095
  specify "should handle implicit aliases in arguments" do
3933
- @ds.set_fr_yield([{:a=>1, :b=>2}, {:a=>3, :b=>4}])
3934
4096
  @ds.select_hash(:c___a, :d___b).should == {1=>2, 3=>4}
3935
4097
  @ds.db.sqls.should == ['SELECT c AS a, d AS b FROM t']
3936
4098
  end
3937
4099
 
3938
4100
  specify "should handle implicit qualifiers and aliases in arguments" do
3939
- @ds.set_fr_yield([{:a=>1, :b=>2}, {:a=>3, :b=>4}])
3940
4101
  @ds.select_hash(:t__c___a, :t__d___b).should == {1=>2, 3=>4}
3941
4102
  @ds.db.sqls.should == ['SELECT t.c AS a, t.d AS b FROM t']
3942
4103
  end
4104
+
4105
+ specify "should handle SQL::Identifiers in arguments" do
4106
+ @ds.select_hash(Sequel.identifier(:a), Sequel.identifier(:b)).should == {1=>2, 3=>4}
4107
+ @ds.db.sqls.should == ['SELECT a, b FROM t']
4108
+ end
4109
+
4110
+ specify "should handle SQL::QualifiedIdentifiers in arguments" do
4111
+ @ds.select_hash(Sequel.qualify(:t, :a), Sequel.identifier(:b).qualify(:t)).should == {1=>2, 3=>4}
4112
+ @ds.db.sqls.should == ['SELECT t.a, t.b FROM t']
4113
+ end
4114
+
4115
+ specify "should handle SQL::AliasedExpressions in arguments" do
4116
+ @ds.select_hash(Sequel.expr(:c).as(:a), Sequel.expr(:t).as(:b)).should == {1=>2, 3=>4}
4117
+ @ds.db.sqls.should == ['SELECT c AS a, t AS b FROM t']
4118
+ end
4119
+
4120
+ specify "should work with arrays of columns" do
4121
+ @db.fetch = [{:a=>1, :b=>2, :c=>3}, {:a=>4, :b=>5, :c=>6}]
4122
+ @ds.select_hash([:a, :c], :b).should == {[1, 3]=>2, [4, 6]=>5}
4123
+ @ds.db.sqls.should == ['SELECT a, c, b FROM t']
4124
+ @ds.select_hash(:a, [:b, :c]).should == {1=>[2, 3], 4=>[5, 6]}
4125
+ @ds.db.sqls.should == ['SELECT a, b, c FROM t']
4126
+ @ds.select_hash([:a, :b], [:b, :c]).should == {[1, 2]=>[2, 3], [4, 5]=>[5, 6]}
4127
+ @ds.db.sqls.should == ['SELECT a, b, b, c FROM t']
4128
+ end
4129
+
4130
+ specify "should raise an error if the resulting symbol cannot be determined" do
4131
+ proc{@ds.select_hash(Sequel.expr(:c).as(:a), Sequel.function(:b))}.should raise_error(Sequel::Error)
4132
+ end
4133
+ end
4134
+
4135
+ describe "Sequel::Dataset#select_hash_groups" do
4136
+ before do
4137
+ @db = Sequel.mock(:fetch=>[{:a=>1, :b=>2}, {:a=>3, :b=>4}])
4138
+ @ds = @db[:t]
4139
+ end
4140
+
4141
+ specify "should do select and to_hash in one step" do
4142
+ @ds.select_hash_groups(:a, :b).should == {1=>[2], 3=>[4]}
4143
+ @ds.db.sqls.should == ['SELECT a, b FROM t']
4144
+ end
4145
+
4146
+ specify "should handle implicit qualifiers in arguments" do
4147
+ @ds.select_hash_groups(:t__a, :t__b).should == {1=>[2], 3=>[4]}
4148
+ @ds.db.sqls.should == ['SELECT t.a, t.b FROM t']
4149
+ end
4150
+
4151
+ specify "should handle implicit aliases in arguments" do
4152
+ @ds.select_hash_groups(:c___a, :d___b).should == {1=>[2], 3=>[4]}
4153
+ @ds.db.sqls.should == ['SELECT c AS a, d AS b FROM t']
4154
+ end
4155
+
4156
+ specify "should handle implicit qualifiers and aliases in arguments" do
4157
+ @ds.select_hash_groups(:t__c___a, :t__d___b).should == {1=>[2], 3=>[4]}
4158
+ @ds.db.sqls.should == ['SELECT t.c AS a, t.d AS b FROM t']
4159
+ end
4160
+
4161
+ specify "should handle SQL::Identifiers in arguments" do
4162
+ @ds.select_hash_groups(Sequel.identifier(:a), Sequel.identifier(:b)).should == {1=>[2], 3=>[4]}
4163
+ @ds.db.sqls.should == ['SELECT a, b FROM t']
4164
+ end
4165
+
4166
+ specify "should handle SQL::QualifiedIdentifiers in arguments" do
4167
+ @ds.select_hash_groups(Sequel.qualify(:t, :a), Sequel.identifier(:b).qualify(:t)).should == {1=>[2], 3=>[4]}
4168
+ @ds.db.sqls.should == ['SELECT t.a, t.b FROM t']
4169
+ end
4170
+
4171
+ specify "should handle SQL::AliasedExpressions in arguments" do
4172
+ @ds.select_hash_groups(Sequel.expr(:c).as(:a), Sequel.expr(:t).as(:b)).should == {1=>[2], 3=>[4]}
4173
+ @ds.db.sqls.should == ['SELECT c AS a, t AS b FROM t']
4174
+ end
4175
+
4176
+ specify "should work with arrays of columns" do
4177
+ @db.fetch = [{:a=>1, :b=>2, :c=>3}, {:a=>4, :b=>5, :c=>6}]
4178
+ @ds.select_hash_groups([:a, :c], :b).should == {[1, 3]=>[2], [4, 6]=>[5]}
4179
+ @ds.db.sqls.should == ['SELECT a, c, b FROM t']
4180
+ @ds.select_hash_groups(:a, [:b, :c]).should == {1=>[[2, 3]], 4=>[[5, 6]]}
4181
+ @ds.db.sqls.should == ['SELECT a, b, c FROM t']
4182
+ @ds.select_hash_groups([:a, :b], [:b, :c]).should == {[1, 2]=>[[2, 3]], [4, 5]=>[[5, 6]]}
4183
+ @ds.db.sqls.should == ['SELECT a, b, b, c FROM t']
4184
+ end
4185
+
4186
+ specify "should raise an error if the resulting symbol cannot be determined" do
4187
+ proc{@ds.select_hash_groups(Sequel.expr(:c).as(:a), Sequel.function(:b))}.should raise_error(Sequel::Error)
4188
+ end
3943
4189
  end
3944
4190
 
3945
4191
  describe "Modifying joined datasets" do
3946
4192
  before do
3947
- @ds = MockDatabase.new.from(:b, :c).join(:d, [:id]).where(:id => 2)
4193
+ @ds = Sequel.mock.from(:b, :c).join(:d, [:id]).where(:id => 2)
3948
4194
  @ds.meta_def(:supports_modifying_joins?){true}
3949
- @ds.db.reset
3950
4195
  end
3951
4196
 
3952
4197
  specify "should allow deleting from joined datasets" do
@@ -3962,7 +4207,7 @@ end
3962
4207
 
3963
4208
  describe "Dataset#lock_style and for_update" do
3964
4209
  before do
3965
- @ds = MockDatabase.new[:t]
4210
+ @ds = Sequel::Dataset.new(nil).from(:t)
3966
4211
  end
3967
4212
 
3968
4213
  specify "#for_update should use FOR UPDATE" do
@@ -3977,3 +4222,173 @@ describe "Dataset#lock_style and for_update" do
3977
4222
  @ds.lock_style("FOR SHARE").sql.should == "SELECT * FROM t FOR SHARE"
3978
4223
  end
3979
4224
  end
4225
+
4226
+ describe "Custom ASTTransformer" do
4227
+ specify "should transform given objects" do
4228
+ c = Class.new(Sequel::ASTTransformer) do
4229
+ def v(s)
4230
+ (s.is_a?(Symbol) || s.is_a?(String)) ? :"#{s}#{s}" : super
4231
+ end
4232
+ end.new
4233
+ ds = Sequel::Dataset.new(nil).from(:t).cross_join(:a___g).join(:b___h, [:c]).join(:d___i, :e=>:f)
4234
+ 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)'
4235
+ 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)'
4236
+ end
4237
+ end
4238
+
4239
+ describe "Dataset#returning" do
4240
+ before do
4241
+ @ds = Sequel.mock(:fetch=>proc{|s| {:foo=>s}})[:t].returning(:foo)
4242
+ @pr = proc do
4243
+ [:insert, :update, :delete].each do |m|
4244
+ @ds.meta_def(:"#{m}_clause_methods"){super() + [:"#{m}_returning_sql"]}
4245
+ end
4246
+ end
4247
+ end
4248
+
4249
+ specify "should use RETURNING clause in the SQL if the dataset supports it" do
4250
+ @pr.call
4251
+ @ds.delete_sql.should == "DELETE FROM t RETURNING foo"
4252
+ @ds.insert_sql(1).should == "INSERT INTO t VALUES (1) RETURNING foo"
4253
+ @ds.update_sql(:foo=>1).should == "UPDATE t SET foo = 1 RETURNING foo"
4254
+ end
4255
+
4256
+ specify "should not use RETURNING clause in the SQL if the dataset does not support it" do
4257
+ @ds.delete_sql.should == "DELETE FROM t"
4258
+ @ds.insert_sql(1).should == "INSERT INTO t VALUES (1)"
4259
+ @ds.update_sql(:foo=>1).should == "UPDATE t SET foo = 1"
4260
+ end
4261
+
4262
+ specify "should have insert, update, and delete yield to blocks if RETURNING is used" do
4263
+ @pr.call
4264
+ h = {}
4265
+ @ds.delete{|r| h = r}
4266
+ h.should == {:foo=>"DELETE FROM t RETURNING foo"}
4267
+ @ds.insert(1){|r| h = r}
4268
+ h.should == {:foo=>"INSERT INTO t VALUES (1) RETURNING foo"}
4269
+ @ds.update(:foo=>1){|r| h = r}
4270
+ h.should == {:foo=>"UPDATE t SET foo = 1 RETURNING foo"}
4271
+ end
4272
+
4273
+ specify "should have insert, update, and delete return arrays of hashes if RETURNING is used and a block is not given" do
4274
+ @pr.call
4275
+ h = {}
4276
+ @ds.delete.should == [{:foo=>"DELETE FROM t RETURNING foo"}]
4277
+ @ds.insert(1).should == [{:foo=>"INSERT INTO t VALUES (1) RETURNING foo"}]
4278
+ @ds.update(:foo=>1).should == [{:foo=>"UPDATE t SET foo = 1 RETURNING foo"}]
4279
+ end
4280
+ end
4281
+
4282
+ describe "Dataset emulating bitwise operator support" do
4283
+ before do
4284
+ @ds = Sequel::Database.new.dataset
4285
+ @ds.quote_identifiers = true
4286
+ def @ds.complex_expression_sql_append(sql, op, args)
4287
+ sql << complex_expression_arg_pairs(args){|a, b| "bitand(#{literal(a)}, #{literal(b)})"}
4288
+ end
4289
+ end
4290
+
4291
+ it "should work with any numbers of arguments for operators" do
4292
+ @ds.select(Sequel::SQL::ComplexExpression.new(:&, :x)).sql.should == 'SELECT "x"'
4293
+ @ds.select(Sequel.expr(:x) & 1).sql.should == 'SELECT bitand("x", 1)'
4294
+ @ds.select(Sequel.expr(:x) & 1 & 2).sql.should == 'SELECT bitand(bitand("x", 1), 2)'
4295
+ end
4296
+ end
4297
+
4298
+ describe "Dataset feature defaults" do
4299
+ it "should not require aliases for recursive CTEs by default" do
4300
+ Sequel::Database.new.dataset.recursive_cte_requires_column_aliases?.should be_false
4301
+ end
4302
+
4303
+ it "should not require placeholder type specifiers by default" do
4304
+ Sequel::Database.new.dataset.requires_placeholder_type_specifiers?.should be_false
4305
+ end
4306
+
4307
+ it "offset use should be returning a separate row number column by default" do
4308
+ Sequel::Database.new.dataset.send(:offset_returns_row_number_column?).should be_false
4309
+ end
4310
+ end
4311
+
4312
+ describe "Dataset extensions" do
4313
+ before(:all) do
4314
+ class << Sequel
4315
+ alias _extension extension
4316
+ def extension(*)
4317
+ end
4318
+ end
4319
+ end
4320
+ after(:all) do
4321
+ class << Sequel
4322
+ alias extension _extension
4323
+ end
4324
+ end
4325
+ before do
4326
+ @ds = Sequel::Dataset.new(nil)
4327
+ end
4328
+
4329
+ specify "should be able to register an extension with a module Database#extension extend the module" do
4330
+ Sequel::Dataset.register_extension(:foo, Module.new{def a; 1; end})
4331
+ @ds.extension(:foo).a.should == 1
4332
+ end
4333
+
4334
+ specify "should be able to register an extension with a block and Database#extension call the block" do
4335
+ @ds.quote_identifiers = false
4336
+ Sequel::Dataset.register_extension(:foo){|db| db.quote_identifiers = true}
4337
+ @ds.extension(:foo).quote_identifiers?.should be_true
4338
+ end
4339
+
4340
+ specify "should be able to register an extension with a callable and Database#extension call the callable" do
4341
+ @ds.quote_identifiers = false
4342
+ Sequel::Dataset.register_extension(:foo, proc{|db| db.quote_identifiers = true})
4343
+ @ds.extension(:foo).quote_identifiers?.should be_true
4344
+ end
4345
+
4346
+ specify "should be able to load multiple extensions in the same call" do
4347
+ @ds.quote_identifiers = false
4348
+ @ds.identifier_input_method = :downcase
4349
+ Sequel::Dataset.register_extension(:foo, proc{|ds| ds.quote_identifiers = true})
4350
+ Sequel::Dataset.register_extension(:bar, proc{|ds| ds.identifier_input_method = nil})
4351
+ ds = @ds.extension(:foo, :bar)
4352
+ ds.quote_identifiers?.should be_true
4353
+ ds.identifier_input_method.should be_nil
4354
+ end
4355
+
4356
+ specify "should have #extension not modify the receiver" do
4357
+ Sequel::Dataset.register_extension(:foo, Module.new{def a; 1; end})
4358
+ @ds.extension(:foo)
4359
+ proc{@ds.a}.should raise_error(NoMethodError)
4360
+ end
4361
+
4362
+ specify "should have #extension not return a cloned dataset" do
4363
+ @ds.extend(Module.new{def b; 2; end})
4364
+ Sequel::Dataset.register_extension(:foo, Module.new{def a; 1; end})
4365
+ v = @ds.extension(:foo)
4366
+ v.should_not equal(@ds)
4367
+ v.should be_a_kind_of(Sequel::Dataset)
4368
+ v.b.should == 2
4369
+ end
4370
+
4371
+ specify "should have #extension! modify the receiver" do
4372
+ Sequel::Dataset.register_extension(:foo, Module.new{def a; 1; end})
4373
+ @ds.extension!(:foo)
4374
+ @ds.a.should == 1
4375
+ end
4376
+
4377
+ specify "should have #extension! return the receiver" do
4378
+ Sequel::Dataset.register_extension(:foo, Module.new{def a; 1; end})
4379
+ @ds.extension!(:foo).should equal(@ds)
4380
+ end
4381
+
4382
+ specify "should register a Database extension for modifying all datasets when registering with a module" do
4383
+ Sequel::Dataset.register_extension(:foo, Module.new{def a; 1; end})
4384
+ Sequel.mock.extension(:foo).dataset.a.should == 1
4385
+ end
4386
+
4387
+ specify "should raise an Error if registering with both a module and a block" do
4388
+ proc{Sequel::Dataset.register_extension(:foo, Module.new){}}.should raise_error(Sequel::Error)
4389
+ end
4390
+
4391
+ specify "should raise an Error if attempting to load an incompatible extension" do
4392
+ proc{@ds.extension(:foo2)}.should raise_error(Sequel::Error)
4393
+ end
4394
+ end