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
data/lib/sequel/sql.rb CHANGED
@@ -38,6 +38,16 @@ module Sequel
38
38
  class LiteralString < ::String
39
39
  end
40
40
 
41
+ # Time subclass that gets literalized with only the time value, so it operates
42
+ # like a standard SQL time type.
43
+ class SQLTime < ::Time
44
+ # Create a new SQLTime instance given an hour, minute, and second.
45
+ def self.create(hour, minute, second, usec = 0)
46
+ t = now
47
+ local(t.year, t.month, t.day, hour, minute, second, usec)
48
+ end
49
+ end
50
+
41
51
  # The SQL module holds classes whose instances represent SQL fragments.
42
52
  # It also holds modules that are included in core ruby classes that
43
53
  # make Sequel a friendly DSL.
@@ -50,28 +60,38 @@ module Sequel
50
60
 
51
61
  # Base class for all SQL expression objects.
52
62
  class Expression
53
- # Expression objects are assumed to be value objects, where their
54
- # attribute values can't change after assignment. In order to make
55
- # it easy to define equality and hash methods, subclass
56
- # instances assume that the only values that affect the results of
57
- # such methods are the values of the object's attributes.
58
- def self.attr_reader(*args)
59
- super
60
- comparison_attrs.concat args
61
- end
63
+ @comparison_attrs = []
64
+
65
+ class << self
66
+ # All attributes used for equality and hash methods.
67
+ attr_reader :comparison_attrs
68
+
69
+ # Expression objects are assumed to be value objects, where their
70
+ # attribute values can't change after assignment. In order to make
71
+ # it easy to define equality and hash methods, subclass
72
+ # instances assume that the only values that affect the results of
73
+ # such methods are the values of the object's attributes.
74
+ def attr_reader(*args)
75
+ super
76
+ comparison_attrs.concat(args)
77
+ end
62
78
 
63
- # All attributes used for equality and hash methods.
64
- def self.comparison_attrs
65
- @comparison_attrs ||= self == Expression ? [] : superclass.comparison_attrs.clone
66
- end
79
+ # Copy the comparison_attrs into the subclass.
80
+ def inherited(subclass)
81
+ super
82
+ subclass.instance_variable_set(:@comparison_attrs, comparison_attrs.dup)
83
+ end
84
+
85
+ private
67
86
 
68
- # Create a to_s instance method that takes a dataset, and calls
69
- # the method provided on the dataset with args as the argument (self by default).
70
- # Used to DRY up some code.
71
- def self.to_s_method(meth, args=:self) # :nodoc:
72
- class_eval("def to_s(ds); ds.#{meth}(#{args}) end", __FILE__, __LINE__)
87
+ # Create a to_s instance method that takes a dataset, and calls
88
+ # the method provided on the dataset with args as the argument (self by default).
89
+ # Used to DRY up some code.
90
+ def to_s_method(meth, args=:self) # :nodoc:
91
+ class_eval("def to_s(ds) ds.#{meth}(#{args}) end", __FILE__, __LINE__)
92
+ class_eval("def to_s_append(ds, sql) ds.#{meth}_append(sql, #{args}) end", __FILE__, __LINE__)
93
+ end
73
94
  end
74
- private_class_method :to_s_method
75
95
 
76
96
  # Alias of <tt>eql?</tt>
77
97
  def ==(other)
@@ -127,7 +147,10 @@ module Sequel
127
147
  MATHEMATICAL_OPERATORS = [:+, :-, :/, :*]
128
148
 
129
149
  # Bitwise mathematical operators used in +NumericMethods+
130
- BITWISE_OPERATORS = [:&, :|, :^, :<<, :>>]
150
+ BITWISE_OPERATORS = [:&, :|, :^, :<<, :>>, :%]
151
+
152
+ # Operators that check for equality
153
+ EQUALITY_OPERATORS = [:'=', :'!=']
131
154
 
132
155
  # Inequality operators used in +InequalityMethods+
133
156
  INEQUALITY_OPERATORS = [:<, :>, :<=, :>=]
@@ -141,23 +164,30 @@ module Sequel
141
164
  # Operators that use IS, used for special casing to override literal true/false values
142
165
  IS_OPERATORS = [:IS, :'IS NOT']
143
166
 
167
+ # Operators that do pattern matching via regular expressions
168
+ REGEXP_OPERATORS = [:~, :'!~', :'~*', :'!~*']
169
+
170
+ # Operators that do pattern matching via LIKE
171
+ LIKE_OPERATORS = [:LIKE, :'NOT LIKE', :ILIKE, :'NOT ILIKE']
172
+
144
173
  # Operator symbols that take exactly two arguments
145
- TWO_ARITY_OPERATORS = [:'=', :'!=', :LIKE, :'NOT LIKE', \
146
- :~, :'!~', :'~*', :'!~*', :ILIKE, :'NOT ILIKE'] + \
147
- INEQUALITY_OPERATORS + BITWISE_OPERATORS + IS_OPERATORS + IN_OPERATORS
174
+ TWO_ARITY_OPERATORS = EQUALITY_OPERATORS + INEQUALITY_OPERATORS + IS_OPERATORS + IN_OPERATORS + REGEXP_OPERATORS + LIKE_OPERATORS
148
175
 
149
176
  # Operator symbols that take one or more arguments
150
- N_ARITY_OPERATORS = [:AND, :OR, :'||'] + MATHEMATICAL_OPERATORS
177
+ N_ARITY_OPERATORS = [:AND, :OR, :'||'] + MATHEMATICAL_OPERATORS + BITWISE_OPERATORS
151
178
 
152
179
  # Operator symbols that take only a single argument
153
180
  ONE_ARITY_OPERATORS = [:NOT, :NOOP, :'B~']
154
181
 
155
- # An array of args for this object
156
- attr_reader :args
182
+ # Custom expressions that may have different syntax on different databases
183
+ CUSTOM_EXPRESSIONS = [:extract]
157
184
 
158
185
  # The operator symbol for this object
159
186
  attr_reader :op
160
187
 
188
+ # An array of args for this object
189
+ attr_reader :args
190
+
161
191
  # Set the operator symbol and arguments for this object to the ones given.
162
192
  # Convert all args that are hashes or arrays of two element arrays to +BooleanExpressions+,
163
193
  # other than the second arg for an IN/NOT IN operator.
@@ -180,6 +210,8 @@ module Sequel
180
210
  args[1] = orig_args[1] if IN_OPERATORS.include?(op)
181
211
  when *ONE_ARITY_OPERATORS
182
212
  raise(Error, "The #{op} operator requires a single argument") unless args.length == 1
213
+ when *CUSTOM_EXPRESSIONS
214
+ # nothing
183
215
  else
184
216
  raise(Error, "Invalid operator #{op}")
185
217
  end
@@ -201,7 +233,7 @@ module Sequel
201
233
  module AliasMethods
202
234
  # Create an SQL alias (+AliasedExpression+) of the receiving column or expression to the given alias.
203
235
  #
204
- # :column.as(:alias) # "column" AS "alias"
236
+ # Sequel.function(:func).as(:alias) # func() AS "alias"
205
237
  def as(aliaz)
206
238
  AliasedExpression.new(self, aliaz)
207
239
  end
@@ -219,14 +251,7 @@ module Sequel
219
251
  # ~:a.sql_number # ~"a"
220
252
  module BitwiseMethods
221
253
  ComplexExpression::BITWISE_OPERATORS.each do |o|
222
- define_method(o) do |ce|
223
- case ce
224
- when BooleanExpression, StringExpression
225
- raise(Sequel::Error, "cannot apply #{o} to a non-numeric expression")
226
- else
227
- NumericExpression.new(o, self, ce)
228
- end
229
- end
254
+ module_eval("def #{o}(o) NumericExpression.new(#{o.inspect}, self, o) end", __FILE__, __LINE__)
230
255
  end
231
256
 
232
257
  # Do the bitwise compliment of the self
@@ -244,16 +269,24 @@ module Sequel
244
269
  # :a & :b # "a" AND "b"
245
270
  # :a | :b # "a" OR "b"
246
271
  # ~:a # NOT "a"
272
+ #
273
+ # One exception to this is when a NumericExpression or Integer is the argument
274
+ # to & or |, in which case a bitwise method will be used:
275
+ #
276
+ # :a & 1 # "a" & 1
277
+ # :a | (:b + 1) # "a" | ("b" + 1)
247
278
  module BooleanMethods
248
279
  ComplexExpression::BOOLEAN_OPERATOR_METHODS.each do |m, o|
249
- define_method(m) do |ce|
250
- case ce
251
- when NumericExpression, StringExpression
252
- raise(Sequel::Error, "cannot apply #{o} to a non-boolean expression")
253
- else
254
- BooleanExpression.new(o, self, ce)
280
+ module_eval(<<-END, __FILE__, __LINE__+1)
281
+ def #{m}(o)
282
+ case o
283
+ when NumericExpression, Integer
284
+ NumericExpression.new(#{m.inspect}, self, o)
285
+ else
286
+ BooleanExpression.new(#{o.inspect}, self, o)
287
+ end
255
288
  end
256
- end
289
+ END
257
290
  end
258
291
 
259
292
  # Create a new BooleanExpression with NOT, representing the inversion of whatever self represents.
@@ -264,13 +297,353 @@ module Sequel
264
297
  end
265
298
  end
266
299
 
300
+ # These methods are designed as replacements for the core extensions, so that
301
+ # Sequel is still easy to use if the core extensions are not enabled.
302
+ module Builders
303
+ # Create an SQL::AliasedExpression for the given expression and alias.
304
+ #
305
+ # Sequel.as(:column, :alias) # "column" AS "alias"
306
+ def as(exp, aliaz)
307
+ SQL::AliasedExpression.new(exp, aliaz)
308
+ end
309
+
310
+ # Order the given argument ascending.
311
+ # Options:
312
+ #
313
+ # :nulls :: Set to :first to use NULLS FIRST (so NULL values are ordered
314
+ # before other values), or :last to use NULLS LAST (so NULL values
315
+ # are ordered after other values).
316
+ #
317
+ # Sequel.asc(:a) # a ASC
318
+ # Sequel.asc(:b, :nulls=>:last) # b ASC NULLS LAST
319
+ def asc(arg, opts={})
320
+ SQL::OrderedExpression.new(arg, false, opts)
321
+ end
322
+
323
+ # Return an <tt>SQL::Blob</tt> that holds the same data as this string.
324
+ # Blobs provide proper escaping of binary data. If given a blob, returns it
325
+ # directly.
326
+ def blob(s)
327
+ if s.is_a?(SQL::Blob)
328
+ s
329
+ else
330
+ SQL::Blob.new(s)
331
+ end
332
+ end
333
+
334
+ # Return an <tt>SQL::CaseExpression</tt> created with the given arguments.
335
+ #
336
+ # Sequel.case([[{:a=>[2,3]}, 1]], 0) # SQL: CASE WHEN a IN (2, 3) THEN 1 ELSE 0 END
337
+ # Sequel.case({:a=>1}, 0, :b) # SQL: CASE b WHEN a THEN 1 ELSE 0 END
338
+ def case(*args) # core_sql ignore
339
+ SQL::CaseExpression.new(*args)
340
+ end
341
+
342
+ # Cast the reciever to the given SQL type. You can specify a ruby class as a type,
343
+ # and it is handled similarly to using a database independent type in the schema methods.
344
+ #
345
+ # Sequel.cast(:a, :integer) # CAST(a AS integer)
346
+ # Sequel.cast(:a, String) # CAST(a AS varchar(255))
347
+ def cast(arg, sql_type)
348
+ SQL::Cast.new(arg, sql_type)
349
+ end
350
+
351
+ # Cast the reciever to the given SQL type (or the database's default Integer type if none given),
352
+ # and return the result as a +NumericExpression+, so you can use the bitwise operators
353
+ # on the result.
354
+ #
355
+ # Sequel.cast_numeric(:a) # CAST(a AS integer)
356
+ # Sequel.cast_numeric(:a, Float) # CAST(a AS double precision)
357
+ def cast_numeric(arg, sql_type = nil)
358
+ cast(arg, sql_type || Integer).sql_number
359
+ end
360
+
361
+ # Cast the reciever to the given SQL type (or the database's default String type if none given),
362
+ # and return the result as a +StringExpression+, so you can use +
363
+ # directly on the result for SQL string concatenation.
364
+ #
365
+ # Sequel.cast_string(:a) # CAST(a AS varchar(255))
366
+ # Sequel.cast_string(:a, :text) # CAST(a AS text)
367
+ def cast_string(arg, sql_type = nil)
368
+ cast(arg, sql_type || String).sql_string
369
+ end
370
+
371
+ # Return an emulated function call for getting the number of characters
372
+ # in the argument:
373
+ #
374
+ # Sequel.char_length(:a) # char_length(a) -- Most databases
375
+ # Sequel.char_length(:a) # length(a) -- SQLite
376
+ def char_length(arg)
377
+ SQL::EmulatedFunction.new(:char_length, arg)
378
+ end
379
+
380
+ # Return a delayed evaluation that uses the passed block. This is used
381
+ # to delay evaluations of the code to runtime. For example, with
382
+ # the following code:
383
+ #
384
+ # ds = DB[:table].where{column > Time.now}
385
+ #
386
+ # The filter is fixed to the time that where was called. Unless you are
387
+ # only using the dataset once immediately after creating it, that's
388
+ # probably not desired. If you just want to set it to the time when the
389
+ # query is sent to the database, you can wrap it in Sequel.delay:
390
+ #
391
+ # ds = DB[:table].where{column > Sequel.delay{Time.now}}
392
+ #
393
+ # Note that for dates and timestamps, you are probably better off using
394
+ # Sequel::CURRENT_DATE and Sequel::CURRENT_TIMESTAMP instead of this
395
+ # generic delayed evaluation facility.
396
+ def delay(&block)
397
+ raise(Error, "Sequel.delay requires a block") unless block
398
+ SQL::DelayedEvaluation.new(block)
399
+ end
400
+
401
+ # Order the given argument descending.
402
+ # Options:
403
+ #
404
+ # :nulls :: Set to :first to use NULLS FIRST (so NULL values are ordered
405
+ # before other values), or :last to use NULLS LAST (so NULL values
406
+ # are ordered after other values).
407
+ #
408
+ # Sequel.desc(:a) # b DESC
409
+ # Sequel.desc(:b, :nulls=>:first) # b DESC NULLS FIRST
410
+ def desc(arg, opts={})
411
+ SQL::OrderedExpression.new(arg, true, opts)
412
+ end
413
+
414
+ # Wraps the given object in an appropriate Sequel wrapper.
415
+ # If the given object is already a Sequel object, return it directly.
416
+ # For condition specifiers (hashes and arrays of two pairs), true, and false,
417
+ # return a boolean expressions. For numeric objects, return a numeric
418
+ # expression. For strings, return a string expression. For procs or when
419
+ # the method is passed a block, evaluate it as a virtual row and wrap it
420
+ # appropriately. In all other cases, use a generic wrapper.
421
+ #
422
+ # This method allows you to construct SQL expressions that are difficult
423
+ # to construct via other methods. For example:
424
+ #
425
+ # Sequel.expr(1) - :a # SQL: (1 - a)
426
+ def expr(arg=(no_arg=true), &block)
427
+ if block_given?
428
+ if no_arg
429
+ return expr(block)
430
+ else
431
+ raise Error, 'cannot provide both an argument and a block to Sequel.expr'
432
+ end
433
+ elsif no_arg
434
+ raise Error, 'must provide either an argument or a block to Sequel.expr'
435
+ end
436
+
437
+ case arg
438
+ when Symbol
439
+ t, c, a = Sequel.split_symbol(arg)
440
+
441
+ arg = if t
442
+ SQL::QualifiedIdentifier.new(t, c)
443
+ else
444
+ SQL::Identifier.new(c)
445
+ end
446
+
447
+ if a
448
+ arg = SQL::AliasedExpression.new(arg, a)
449
+ end
450
+
451
+ arg
452
+ when SQL::Expression, LiteralString, SQL::Blob
453
+ arg
454
+ when Hash
455
+ SQL::BooleanExpression.from_value_pairs(arg, :AND)
456
+ when Array
457
+ if condition_specifier?(arg)
458
+ SQL::BooleanExpression.from_value_pairs(arg, :AND)
459
+ else
460
+ SQL::Wrapper.new(arg)
461
+ end
462
+ when Numeric
463
+ SQL::NumericExpression.new(:NOOP, arg)
464
+ when String
465
+ SQL::StringExpression.new(:NOOP, arg)
466
+ when TrueClass, FalseClass
467
+ SQL::BooleanExpression.new(:NOOP, arg)
468
+ when Proc
469
+ expr(virtual_row(&arg))
470
+ else
471
+ SQL::Wrapper.new(arg)
472
+ end
473
+ end
474
+
475
+ # Extract a datetime_part (e.g. year, month) from the given
476
+ # expression:
477
+ #
478
+ # Sequel.extract(:year, :date) # extract(year FROM "date")
479
+ def extract(datetime_part, exp)
480
+ SQL::NumericExpression.new(:extract, datetime_part, exp)
481
+ end
482
+
483
+ # Returns a <tt>Sequel::SQL::Function</tt> with the function name
484
+ # and the given arguments.
485
+ #
486
+ # Sequel.function(:now) # SQL: now()
487
+ # Sequel.function(:substr, :a, 1) # SQL: substr(a, 1)
488
+ def function(name, *args)
489
+ SQL::Function.new(name, *args)
490
+ end
491
+
492
+ # Return the argument wrapped as an <tt>SQL::Identifier</tt>.
493
+ #
494
+ # Sequel.identifier(:a__b) # "a__b"
495
+ def identifier(name)
496
+ SQL::Identifier.new(name)
497
+ end
498
+
499
+ # Return a <tt>Sequel::SQL::StringExpression</tt> representing an SQL string made up of the
500
+ # concatenation of the given array's elements. If an argument is passed,
501
+ # it is used in between each element of the array in the SQL
502
+ # concatenation.
503
+ #
504
+ # Sequel.join([:a]) # SQL: a
505
+ # Sequel.join([:a, :b]) # SQL: a || b
506
+ # Sequel.join([:a, 'b']) # SQL: a || 'b'
507
+ # Sequel.join(['a', :b], ' ') # SQL: 'a' || ' ' || b
508
+ def join(args, joiner=nil)
509
+ raise Error, 'argument to Sequel.join must be an array' unless args.is_a?(Array)
510
+ if joiner
511
+ args = args.zip([joiner]*args.length).flatten
512
+ args.pop
513
+ end
514
+
515
+ return SQL::StringExpression.new(:NOOP, '') if args.empty?
516
+
517
+ args = args.map do |a|
518
+ case a
519
+ when Symbol, ::Sequel::SQL::Expression, ::Sequel::LiteralString, TrueClass, FalseClass, NilClass
520
+ a
521
+ else
522
+ a.to_s
523
+ end
524
+ end
525
+ SQL::StringExpression.new(:'||', *args)
526
+ end
527
+
528
+ # Create a <tt>BooleanExpression</tt> case insensitive (if the database supports it) pattern match of the receiver with
529
+ # the given patterns. See <tt>SQL::StringExpression.like</tt>.
530
+ #
531
+ # Sequel.ilike(:a, 'A%') # "a" ILIKE 'A%'
532
+ def ilike(*args)
533
+ SQL::StringExpression.like(*(args << {:case_insensitive=>true}))
534
+ end
535
+
536
+ # Create a <tt>SQL::BooleanExpression</tt> case sensitive (if the database supports it) pattern match of the receiver with
537
+ # the given patterns. See <tt>SQL::StringExpression.like</tt>.
538
+ #
539
+ # Sequel.like(:a, 'A%') # "a" LIKE 'A%'
540
+ def like(*args)
541
+ SQL::StringExpression.like(*args)
542
+ end
543
+
544
+ # Converts a string into a <tt>Sequel::LiteralString</tt>, in order to override string
545
+ # literalization, e.g.:
546
+ #
547
+ # DB[:items].filter(:abc => 'def').sql #=>
548
+ # "SELECT * FROM items WHERE (abc = 'def')"
549
+ #
550
+ # DB[:items].filter(:abc => Sequel.lit('def')).sql #=>
551
+ # "SELECT * FROM items WHERE (abc = def)"
552
+ #
553
+ # You can also provide arguments, to create a <tt>Sequel::SQL::PlaceholderLiteralString</tt>:
554
+ #
555
+ # DB[:items].select{|o| o.count(Sequel.lit('DISTINCT ?', :a))}.sql #=>
556
+ # "SELECT count(DISTINCT a) FROM items"
557
+ def lit(s, *args) # core_sql ignore
558
+ if args.empty?
559
+ if s.is_a?(LiteralString)
560
+ s
561
+ else
562
+ LiteralString.new(s)
563
+ end
564
+ else
565
+ SQL::PlaceholderLiteralString.new(s, args)
566
+ end
567
+ end
568
+
569
+ # Return a <tt>Sequel::SQL::BooleanExpression</tt> created from the condition
570
+ # specifier, matching none of the conditions.
571
+ #
572
+ # Sequel.negate(:a=>true) # SQL: a IS NOT TRUE
573
+ # Sequel.negate([[:a, true]]) # SQL: a IS NOT TRUE
574
+ # Sequel.negate([[:a, 1], [:b, 2]]) # SQL: ((a != 1) AND (b != 2))
575
+ def negate(arg)
576
+ if condition_specifier?(arg)
577
+ SQL::BooleanExpression.from_value_pairs(arg, :AND, true)
578
+ else
579
+ raise Error, 'must pass a conditions specifier to Sequel.negate'
580
+ end
581
+ end
582
+
583
+ # Return a <tt>Sequel::SQL::BooleanExpression</tt> created from the condition
584
+ # specifier, matching any of the conditions.
585
+ #
586
+ # Sequel.or(:a=>true) # SQL: a IS TRUE
587
+ # Sequel.or([[:a, true]]) # SQL: a IS TRUE
588
+ # Sequel.or([[:a, 1], [:b, 2]]) # SQL: ((a = 1) OR (b = 2))
589
+ def or(arg)
590
+ if condition_specifier?(arg)
591
+ SQL::BooleanExpression.from_value_pairs(arg, :OR, false)
592
+ else
593
+ raise Error, 'must pass a conditions specifier to Sequel.or'
594
+ end
595
+ end
596
+
597
+ # Create a qualified identifier with the given qualifier and identifier
598
+ #
599
+ # Sequel.qualify(:table, :column) # "table"."column"
600
+ # Sequel.qualify(:schema, :table) # "schema"."table"
601
+ # Sequel.qualify(:table, :column).qualify(:schema) # "schema"."table"."column"
602
+ def qualify(qualifier, identifier)
603
+ SQL::QualifiedIdentifier.new(qualifier, identifier)
604
+ end
605
+
606
+ # Return an <tt>SQL::Subscript</tt> with the given arguments, representing an
607
+ # SQL array access.
608
+ #
609
+ # Sequel.subscript(:array, 1) # array[1]
610
+ # Sequel.subscript(:array, 1, 2) # array[1, 2]
611
+ # Sequel.subscript(:array, [1, 2]) # array[1, 2]
612
+ def subscript(exp, *subs)
613
+ SQL::Subscript.new(exp, subs.flatten)
614
+ end
615
+
616
+ # Return an emulated function call for trimming a string of spaces from
617
+ # both sides (similar to ruby's String#strip).
618
+ #
619
+ # Sequel.trim(:a) # trim(a) -- Most databases
620
+ # Sequel.trim(:a) # ltrim(rtrim(a)) -- Microsoft SQL Server
621
+ def trim(arg)
622
+ SQL::EmulatedFunction.new(:trim, arg)
623
+ end
624
+
625
+ # Return a <tt>SQL::ValueList</tt> created from the given array. Used if the array contains
626
+ # all two element arrays and you want it treated as an SQL value list (IN predicate)
627
+ # instead of as a conditions specifier (similar to a hash). This is not necessary if you are using
628
+ # this array as a value in a filter, but may be necessary if you are using it as a
629
+ # value with placeholder SQL:
630
+ #
631
+ # DB[:a].filter([:a, :b]=>[[1, 2], [3, 4]]) # SQL: (a, b) IN ((1, 2), (3, 4))
632
+ # DB[:a].filter('(a, b) IN ?', [[1, 2], [3, 4]]) # SQL: (a, b) IN ((1 = 2) AND (3 = 4))
633
+ # DB[:a].filter('(a, b) IN ?', Sequel.value_list([[1, 2], [3, 4]])) # SQL: (a, b) IN ((1, 2), (3, 4))
634
+ def value_list(arg)
635
+ raise Error, 'argument to Sequel.value_list must be an array' unless arg.is_a?(Array)
636
+ SQL::ValueList.new(arg)
637
+ end
638
+ end
639
+
267
640
  # Holds methods that are used to cast objects to different SQL types.
268
641
  module CastMethods
269
642
  # Cast the reciever to the given SQL type. You can specify a ruby class as a type,
270
643
  # and it is handled similarly to using a database independent type in the schema methods.
271
644
  #
272
- # :a.cast(:integer) # CAST(a AS integer)
273
- # :a.cast(String) # CAST(a AS varchar(255))
645
+ # Sequel.function(:func).cast(:integer) # CAST(func() AS integer)
646
+ # Sequel.function(:func).cast(String) # CAST(func() AS varchar(255))
274
647
  def cast(sql_type)
275
648
  Cast.new(self, sql_type)
276
649
  end
@@ -279,8 +652,8 @@ module Sequel
279
652
  # and return the result as a +NumericExpression+, so you can use the bitwise operators
280
653
  # on the result.
281
654
  #
282
- # :a.cast_numeric # CAST(a AS integer)
283
- # :a.cast_numeric(Float) # CAST(a AS double precision)
655
+ # Sequel.function(:func).cast_numeric # CAST(func() AS integer)
656
+ # Sequel.function(:func).cast_numeric(Float) # CAST(func() AS double precision)
284
657
  def cast_numeric(sql_type = nil)
285
658
  cast(sql_type || Integer).sql_number
286
659
  end
@@ -289,13 +662,13 @@ module Sequel
289
662
  # and return the result as a +StringExpression+, so you can use +
290
663
  # directly on the result for SQL string concatenation.
291
664
  #
292
- # :a.cast_string # CAST(a AS varchar(255))
293
- # :a.cast_string(:text) # CAST(a AS text)
665
+ # Sequel.function(:func).cast_string # CAST(func() AS varchar(255))
666
+ # Sequel.function(:func).cast_string(:text) # CAST(func() AS text)
294
667
  def cast_string(sql_type = nil)
295
668
  cast(sql_type || String).sql_string
296
669
  end
297
670
  end
298
-
671
+
299
672
  # Adds methods that allow you to treat an object as an instance of a specific
300
673
  # +ComplexExpression+ subclass. This is useful if another library
301
674
  # overrides the methods defined by Sequel.
@@ -310,18 +683,14 @@ module Sequel
310
683
  #
311
684
  # :price.sql_number/10 > 100
312
685
  module ComplexExpressionMethods
313
- # Extract a datetime_part (e.g. year, month) from self:
686
+ # Extract a datetime part (e.g. year, month) from self:
314
687
  #
315
688
  # :date.extract(:year) # extract(year FROM "date")
316
689
  #
317
690
  # Also has the benefit of returning the result as a
318
691
  # NumericExpression instead of a generic ComplexExpression.
319
- #
320
- # The extract function is in the SQL standard, but it doesn't
321
- # doesn't use the standard function calling convention, and it
322
- # doesn't work on all databases.
323
692
  def extract(datetime_part)
324
- Function.new(:extract, PlaceholderLiteralString.new("#{datetime_part} FROM ?", [self])).sql_number
693
+ NumericExpression.new(:extract, datetime_part, self)
325
694
  end
326
695
 
327
696
  # Return a BooleanExpression representation of +self+.
@@ -346,55 +715,21 @@ module Sequel
346
715
  end
347
716
  end
348
717
 
349
- # Includes an +identifier+ method that returns <tt>Identifier</tt>s.
350
- module IdentifierMethods
351
- # Return self wrapped as an <tt>SQL::Identifier</tt>.
352
- #
353
- # :a__b # "a"."b"
354
- # :a__b.identifier # "a__b"
355
- def identifier
356
- Identifier.new(self)
357
- end
358
- end
359
-
360
718
  # This module includes the inequality methods (>, <, >=, <=) that are defined on objects that can be
361
- # used in a numeric or string context in SQL (+Symbol+ (except on ruby 1.9), +LiteralString+,
362
- # <tt>SQL::GenericExpression</tt>).
719
+ # used in a numeric or string context in SQL.
363
720
  #
364
- # 'a'.lit > :b # a > "b"
365
- # 'a'.lit < :b # a > "b"
366
- # 'a'.lit >= :b # a >= "b"
367
- # 'a'.lit <= :b # a <= "b"
721
+ # Sequel.lit('a') > :b # a > "b"
722
+ # Sequel.lit('a') < :b # a > "b"
723
+ # Sequel.lit('a') >= :b # a >= "b"
724
+ # Sequel.lit('a') <= :b # a <= "b"
368
725
  module InequalityMethods
369
726
  ComplexExpression::INEQUALITY_OPERATORS.each do |o|
370
- define_method(o) do |ce|
371
- case ce
372
- when BooleanExpression, TrueClass, FalseClass, NilClass, Hash, ::Array
373
- raise(Error, "cannot apply #{o} to a boolean expression")
374
- else
375
- BooleanExpression.new(o, self, ce)
376
- end
377
- end
727
+ module_eval("def #{o}(o) BooleanExpression.new(#{o.inspect}, self, o) end", __FILE__, __LINE__)
378
728
  end
379
729
  end
380
730
 
381
- # This module augments the default initalize method for the
382
- # +ComplexExpression+ subclass it is included in, so that
383
- # attempting to use boolean input when initializing a +NumericExpression+
384
- # or +StringExpression+ results in an error. It is not expected to be
385
- # used directly.
731
+ # Only exists for backwards compatibility, ignore it.
386
732
  module NoBooleanInputMethods
387
- # Raise an +Error+ if one of the args would be boolean in an SQL
388
- # context, otherwise call super.
389
- def initialize(op, *args)
390
- args.each do |a|
391
- case a
392
- when BooleanExpression, TrueClass, FalseClass, NilClass, Hash, ::Array
393
- raise(Error, "cannot apply #{op} to a boolean expression")
394
- end
395
- end
396
- super
397
- end
398
733
  end
399
734
 
400
735
  # This module includes the standard mathematical methods (+, -, *, and /)
@@ -405,15 +740,66 @@ module Sequel
405
740
  # :a - :b # "a" - "b"
406
741
  # :a * :b # "a" * "b"
407
742
  # :a / :b # "a" / "b"
743
+ #
744
+ # One exception to this is if + is called with a +String+ or +StringExpression+,
745
+ # in which case the || operator is used instead of the + operator:
746
+ #
747
+ # :a + 'b' # "a" || 'b'
408
748
  module NumericMethods
409
749
  ComplexExpression::MATHEMATICAL_OPERATORS.each do |o|
410
- define_method(o) do |ce|
411
- case ce
412
- when BooleanExpression, StringExpression
413
- raise(Sequel::Error, "cannot apply #{o} to a non-numeric expression")
414
- else
415
- NumericExpression.new(o, self, ce)
750
+ module_eval("def #{o}(o) NumericExpression.new(#{o.inspect}, self, o) end", __FILE__, __LINE__) unless o == :+
751
+ end
752
+
753
+ # Use || as the operator when called with StringExpression and String instances,
754
+ # and the + operator for LiteralStrings and all other types.
755
+ def +(ce)
756
+ case ce
757
+ when LiteralString
758
+ NumericExpression.new(:+, self, ce)
759
+ when StringExpression, String
760
+ StringExpression.new(:'||', self, ce)
761
+ else
762
+ NumericExpression.new(:+, self, ce)
763
+ end
764
+ end
765
+ end
766
+
767
+ # These methods are designed as replacements for the core extension operator
768
+ # methods, so that Sequel is still easy to use if the core extensions are not
769
+ # enabled.
770
+ #
771
+ # The following methods are defined via metaprogramming: +, -, *, /, &, |.
772
+ # The +, -, *, and / operators return numeric expressions combining all the
773
+ # arguments with the appropriate operator, and the & and | operators return
774
+ # boolean expressions combining all of the arguments with either AND or OR.
775
+ module OperatorBuilders
776
+ %w'+ - * /'.each do |op|
777
+ class_eval(<<-END, __FILE__, __LINE__ + 1)
778
+ def #{op}(*args)
779
+ SQL::NumericExpression.new(:#{op}, *args)
780
+ end
781
+ END
782
+ end
783
+
784
+ {'&'=>'AND', '|'=>'OR'}.each do |m, op|
785
+ class_eval(<<-END, __FILE__, __LINE__ + 1)
786
+ def #{m}(*args)
787
+ SQL::BooleanExpression.new(:#{op}, *args)
416
788
  end
789
+ END
790
+ end
791
+
792
+ # Invert the given expression. Returns a <tt>Sequel::SQL::BooleanExpression</tt>
793
+ # created from this argument, not matching all of the conditions.
794
+ #
795
+ # Sequel.~(nil) # SQL: NOT NULL
796
+ # Sequel.~([[:a, true]]) # SQL: a IS NOT TRUE
797
+ # Sequel.~([[:a, 1], [:b, [2, 3]]]) # SQL: a != 1 OR b NOT IN (2, 3)
798
+ def ~(arg)
799
+ if condition_specifier?(arg)
800
+ SQL::BooleanExpression.from_value_pairs(arg, :OR, true)
801
+ else
802
+ SQL::BooleanExpression.invert(arg)
417
803
  end
418
804
  end
419
805
  end
@@ -443,13 +829,25 @@ module Sequel
443
829
  end
444
830
 
445
831
  # Includes a +qualify+ method that created <tt>QualifiedIdentifier</tt>s, used for qualifying column
446
- # names with a table or table names with a schema.
832
+ # names with a table or table names with a schema, and the * method for returning all columns in
833
+ # the identifier if no arguments are given.
447
834
  module QualifyingMethods
835
+ # If no arguments are given, return an SQL::ColumnAll:
836
+ #
837
+ # Sequel.expr(:a__b).* # a.b.*
838
+ def *(ce=(arg=false;nil))
839
+ if arg == false
840
+ Sequel::SQL::ColumnAll.new(self)
841
+ else
842
+ super(ce)
843
+ end
844
+ end
845
+
448
846
  # Qualify the receiver with the given +qualifier+ (table for column/schema for table).
449
847
  #
450
- # :column.qualify(:table) # "table"."column"
451
- # :table.qualify(:schema) # "schema"."table"
452
- # :column.qualify(:table).qualify(:schema) # "schema"."table"."column"
848
+ # Sequel.expr(:column).qualify(:table) # "table"."column"
849
+ # Sequel.expr(:table).qualify(:schema) # "schema"."table"
850
+ # Sequel.qualify(:table, :column).qualify(:schema) # "schema"."table"."column"
453
851
  def qualify(qualifier)
454
852
  QualifiedIdentifier.new(qualifier, self)
455
853
  end
@@ -523,8 +921,16 @@ module Sequel
523
921
  # stored as a blob type in the database. Sequel represents binary data as a Blob object because
524
922
  # most database engines require binary data to be escaped differently than regular strings.
525
923
  class Blob < ::String
526
- # Returns +self+, used so that Blobs don't get wrapped in multiple
527
- # levels.
924
+ include SQL::AliasMethods
925
+ include SQL::CastMethods
926
+
927
+ # Return a LiteralString with the same content if no args are given, otherwise
928
+ # return a SQL::PlaceholderLiteralString with the current string and the given args.
929
+ def lit(*args)
930
+ args.empty? ? LiteralString.new(self) : SQL::PlaceholderLiteralString.new(self, args)
931
+ end
932
+
933
+ # Returns +self+, since it is already a blob.
528
934
  def to_sequel_blob
529
935
  self
530
936
  end
@@ -556,27 +962,33 @@ module Sequel
556
962
  # ~from_value_pairs(hash)
557
963
  # from_value_pairs(hash, :OR, true)
558
964
  def self.from_value_pairs(pairs, op=:AND, negate=false)
559
- pairs = pairs.collect do |l,r|
560
- ce = case r
561
- when Range
562
- new(:AND, new(:>=, l, r.begin), new(r.exclude_end? ? :< : :<=, l, r.end))
563
- when ::Array, ::Sequel::Dataset
564
- new(:IN, l, r)
565
- when NegativeBooleanConstant
566
- new(:"IS NOT", l, r.constant)
567
- when BooleanConstant
568
- new(:IS, l, r.constant)
569
- when NilClass, TrueClass, FalseClass
570
- new(:IS, l, r)
571
- when Regexp
572
- StringExpression.like(l, r)
573
- else
574
- new(:'=', l, r)
575
- end
576
- negate ? invert(ce) : ce
577
- end
965
+ pairs = pairs.map{|l,r| from_value_pair(l, r)}
966
+ pairs.map!{|ce| invert(ce)} if negate
578
967
  pairs.length == 1 ? pairs.at(0) : new(op, *pairs)
579
968
  end
969
+
970
+ # Return a BooleanExpression based on the right side of the pair.
971
+ def self.from_value_pair(l, r)
972
+ case r
973
+ when Range
974
+ new(:AND, new(:>=, l, r.begin), new(r.exclude_end? ? :< : :<=, l, r.end))
975
+ when ::Array, ::Sequel::Dataset
976
+ new(:IN, l, r)
977
+ when NegativeBooleanConstant
978
+ new(:"IS NOT", l, r.constant)
979
+ when BooleanConstant
980
+ new(:IS, l, r.constant)
981
+ when NilClass, TrueClass, FalseClass
982
+ new(:IS, l, r)
983
+ when Regexp
984
+ StringExpression.like(l, r)
985
+ when DelayedEvaluation
986
+ Sequel.delay{from_value_pair(l, r.callable.call)}
987
+ else
988
+ new(:'=', l, r)
989
+ end
990
+ end
991
+ private_class_method :from_value_pair
580
992
 
581
993
  # Invert the expression, if possible. If the expression cannot
582
994
  # be inverted, raise an error. An inverted expression should match everything that the
@@ -595,10 +1007,27 @@ module Sequel
595
1007
  end
596
1008
  when StringExpression, NumericExpression
597
1009
  raise(Sequel::Error, "cannot invert #{ce.inspect}")
1010
+ when Constant
1011
+ CONSTANT_INVERSIONS[ce] || raise(Sequel::Error, "cannot invert #{ce.inspect}")
598
1012
  else
599
1013
  BooleanExpression.new(:NOT, ce)
600
1014
  end
601
1015
  end
1016
+
1017
+ # Always use an AND operator for & on BooleanExpressions
1018
+ def &(ce)
1019
+ BooleanExpression.new(:AND, self, ce)
1020
+ end
1021
+
1022
+ # Always use an OR operator for | on BooleanExpressions
1023
+ def |(ce)
1024
+ BooleanExpression.new(:OR, self, ce)
1025
+ end
1026
+
1027
+ # Return self instead of creating a new object to save on memory.
1028
+ def sql_boolean
1029
+ self
1030
+ end
602
1031
  end
603
1032
 
604
1033
  # Represents an SQL CASE expression, used for conditional branching in SQL.
@@ -628,6 +1057,17 @@ module Sequel
628
1057
  !@no_expression
629
1058
  end
630
1059
 
1060
+ # Merge the CASE expression into the conditions, useful for databases that
1061
+ # don't support CASE expressions.
1062
+ def with_merged_expression
1063
+ if expression?
1064
+ e = expression
1065
+ CaseExpression.new(conditions.map{|c, r| [::Sequel::SQL::BooleanExpression.new(:'=', e, c), r]}, default)
1066
+ else
1067
+ self
1068
+ end
1069
+ end
1070
+
631
1071
  to_s_method :case_expression_sql
632
1072
  end
633
1073
 
@@ -666,6 +1106,21 @@ module Sequel
666
1106
  include CastMethods
667
1107
  include OrderMethods
668
1108
  include SubscriptMethods
1109
+
1110
+ # Return a BooleanExpression with the same op and args.
1111
+ def sql_boolean
1112
+ BooleanExpression.new(self.op, *self.args)
1113
+ end
1114
+
1115
+ # Return a NumericExpression with the same op and args.
1116
+ def sql_number
1117
+ NumericExpression.new(self.op, *self.args)
1118
+ end
1119
+
1120
+ # Return a StringExpression with the same op and args.
1121
+ def sql_string
1122
+ StringExpression.new(self.op, *self.args)
1123
+ end
669
1124
  end
670
1125
 
671
1126
  # Represents constants or psuedo-constants (e.g. +CURRENT_DATE+) in SQL.
@@ -688,7 +1143,7 @@ module Sequel
688
1143
 
689
1144
  # Represents inverse boolean constants (currently only +NOTNULL+). A
690
1145
  # special class to allow for special behavior.
691
- class NegativeBooleanConstant < BooleanConstant
1146
+ class NegativeBooleanConstant < Constant
692
1147
  to_s_method :negative_boolean_constant_sql, '@constant'
693
1148
  end
694
1149
 
@@ -706,14 +1161,35 @@ module Sequel
706
1161
  NOTNULL = NegativeBooleanConstant.new(nil)
707
1162
  end
708
1163
 
1164
+ class ComplexExpression
1165
+ # A hash of the opposite for each constant, used for inverting constants.
1166
+ CONSTANT_INVERSIONS = {Constants::TRUE=>Constants::FALSE, Constants::FALSE=>Constants::TRUE,
1167
+ Constants::NULL=>Constants::NOTNULL, Constants::NOTNULL=>Constants::NULL}
1168
+ end
1169
+
1170
+ # Represents a delayed evaluation, encapsulating a callable
1171
+ # object which returns the value to use when called.
1172
+ class DelayedEvaluation < GenericExpression
1173
+ # A callable object that returns the value of the evaluation
1174
+ # when called.
1175
+ attr_reader :callable
1176
+
1177
+ # Set the callable object
1178
+ def initialize(callable)
1179
+ @callable = callable
1180
+ end
1181
+
1182
+ to_s_method :delayed_evaluation_sql, '@callable'
1183
+ end
1184
+
709
1185
  # Represents an SQL function call.
710
1186
  class Function < GenericExpression
711
- # The array of arguments to pass to the function (may be blank)
712
- attr_reader :args
713
-
714
1187
  # The SQL function to call
715
1188
  attr_reader :f
716
1189
 
1190
+ # The array of arguments to pass to the function (may be blank)
1191
+ attr_reader :args
1192
+
717
1193
  # Set the functions and args to the given arguments
718
1194
  def initialize(f, *args)
719
1195
  @f, @args = f, args
@@ -721,6 +1197,12 @@ module Sequel
721
1197
 
722
1198
  to_s_method :function_sql
723
1199
  end
1200
+
1201
+ # Represents an SQL function call that is translated/emulated
1202
+ # on databases that lack support for such a function.
1203
+ class EmulatedFunction < Function
1204
+ to_s_method :emulated_function_sql
1205
+ end
724
1206
 
725
1207
  class GenericExpression
726
1208
  include AliasMethods
@@ -806,15 +1288,16 @@ module Sequel
806
1288
  # required for the prepared statement support and for database-specific
807
1289
  # literalization.
808
1290
  class PlaceholderLiteralString < GenericExpression
1291
+ # The literal string containing placeholders. This can also be an array
1292
+ # of strings, where each arg in args goes between the string elements.
1293
+ attr_reader :str
1294
+
809
1295
  # The arguments that will be subsituted into the placeholders.
810
1296
  # Either an array of unnamed placeholders (which will be substituted in
811
1297
  # order for ? characters), or a hash of named placeholders (which will be
812
1298
  # substituted for :key phrases).
813
1299
  attr_reader :args
814
1300
 
815
- # The literal string containing placeholders
816
- attr_reader :str
817
-
818
1301
  # Whether to surround the expression with parantheses
819
1302
  attr_reader :parens
820
1303
 
@@ -834,7 +1317,16 @@ module Sequel
834
1317
  include BitwiseMethods
835
1318
  include NumericMethods
836
1319
  include InequalityMethods
837
- include NoBooleanInputMethods
1320
+
1321
+ # Always use + for + operator for NumericExpressions.
1322
+ def +(ce)
1323
+ NumericExpression.new(:+, self, ce)
1324
+ end
1325
+
1326
+ # Return self instead of creating a new object to save on memory.
1327
+ def sql_number
1328
+ self
1329
+ end
838
1330
  end
839
1331
 
840
1332
  # Represents a column/expression to order the result set by.
@@ -880,18 +1372,18 @@ module Sequel
880
1372
  class QualifiedIdentifier < GenericExpression
881
1373
  include QualifyingMethods
882
1374
 
883
- # The column/table referenced
884
- attr_reader :column
885
-
886
1375
  # The table/schema qualifying the reference
887
1376
  attr_reader :table
888
1377
 
1378
+ # The column/table referenced
1379
+ attr_reader :column
1380
+
889
1381
  # Set the table and column to the given arguments
890
1382
  def initialize(table, column)
891
1383
  @table, @column = table, column
892
1384
  end
893
1385
 
894
- to_s_method :qualified_identifier_sql
1386
+ to_s_method :qualified_identifier_sql, "@table, @column"
895
1387
  end
896
1388
 
897
1389
  # Subclass of +ComplexExpression+ where the expression results
@@ -900,7 +1392,6 @@ module Sequel
900
1392
  include StringMethods
901
1393
  include StringConcatenationMethods
902
1394
  include InequalityMethods
903
- include NoBooleanInputMethods
904
1395
 
905
1396
  # Map of [regexp, case_insenstive] to +ComplexExpression+ operator symbol
906
1397
  LIKE_MAP = {[true, true]=>:'~*', [true, false]=>:~, [false, true]=>:ILIKE, [false, false]=>:LIKE}
@@ -949,6 +1440,11 @@ module Sequel
949
1440
  end
950
1441
  end
951
1442
  private_class_method :like_element
1443
+
1444
+ # Return self instead of creating a new object to save on memory.
1445
+ def sql_string
1446
+ self
1447
+ end
952
1448
  end
953
1449
 
954
1450
  # Represents an SQL array access, with multiple possible arguments.
@@ -966,9 +1462,21 @@ module Sequel
966
1462
 
967
1463
  # Create a new +Subscript+ appending the given subscript(s)
968
1464
  # the the current array of subscripts.
1465
+ #
1466
+ # :a.sql_subscript(2) # a[2]
1467
+ # :a.sql_subscript(2) | 1 # a[2, 1]
969
1468
  def |(sub)
970
1469
  Subscript.new(@f, @sub + Array(sub))
971
1470
  end
1471
+
1472
+ # Create a new +Subscript+ by accessing a subarray of a multidimensional
1473
+ # array.
1474
+ #
1475
+ # :a.sql_subscript(2) # a[2]
1476
+ # :a.sql_subscript(2)[1] # a[2][1]
1477
+ def [](sub)
1478
+ Subscript.new(self, Array(sub))
1479
+ end
972
1480
 
973
1481
  to_s_method :subscript_sql
974
1482
  end
@@ -1019,28 +1527,71 @@ module Sequel
1019
1527
  # Examples:
1020
1528
  #
1021
1529
  # ds = DB[:t]
1530
+ #
1022
1531
  # # Argument yielded to block
1023
1532
  # ds.filter{|r| r.name < 2} # SELECT * FROM t WHERE (name < 2)
1533
+ #
1024
1534
  # # Block without argument (instance_eval)
1025
1535
  # ds.filter{name < 2} # SELECT * FROM t WHERE (name < 2)
1536
+ #
1026
1537
  # # Qualified identifiers
1027
1538
  # ds.filter{table__column + 1 < 2} # SELECT * FROM t WHERE ((table.column + 1) < 2)
1539
+ #
1028
1540
  # # Functions
1029
1541
  # ds.filter{is_active(1, 'arg2')} # SELECT * FROM t WHERE is_active(1, 'arg2')
1030
1542
  # ds.select{version{}} # SELECT version() FROM t
1031
1543
  # ds.select{count(:*){}} # SELECT count(*) FROM t
1032
1544
  # ds.select{count(:distinct, col1){}} # SELECT count(DISTINCT col1) FROM t
1545
+ #
1033
1546
  # # Window Functions
1034
1547
  # ds.select{rank(:over){}} # SELECT rank() OVER () FROM t
1035
1548
  # ds.select{count(:over, :*=>true){}} # SELECT count(*) OVER () FROM t
1036
1549
  # ds.select{sum(:over, :args=>col1, :partition=>col2, :order=>col3){}} # SELECT sum(col1) OVER (PARTITION BY col2 ORDER BY col3) FROM t
1037
1550
  #
1551
+ # # Math Operators
1552
+ # ds.select{|o| o.+(1, :a).as(:b)} # SELECT (1 + a) AS b FROM t
1553
+ # ds.select{|o| o.-(2, :a).as(:b)} # SELECT (2 - a) AS b FROM t
1554
+ # ds.select{|o| o.*(3, :a).as(:b)} # SELECT (3 * a) AS b FROM t
1555
+ # ds.select{|o| o./(4, :a).as(:b)} # SELECT (4 / a) AS b FROM t
1556
+ #
1557
+ # # Boolean Operators
1558
+ # ds.filter{|o| o.&({:a=>1}, :b)} # SELECT * FROM t WHERE ((a = 1) AND b)
1559
+ # ds.filter{|o| o.|({:a=>1}, :b)} # SELECT * FROM t WHERE ((a = 1) OR b)
1560
+ # ds.filter{|o| o.~({:a=>1})} # SELECT * FROM t WHERE (a != 1)
1561
+ # ds.filter{|o| o.~({:a=>1, :b=>2})} # SELECT * FROM t WHERE ((a != 1) OR (b != 2))
1562
+ #
1563
+ # # Inequality Operators
1564
+ # ds.filter{|o| o.>(1, :a)} # SELECT * FROM t WHERE (1 > a)
1565
+ # ds.filter{|o| o.<(2, :a)} # SELECT * FROM t WHERE (2 < a)
1566
+ # ds.filter{|o| o.>=(3, :a)} # SELECT * FROM t WHERE (3 >= a)
1567
+ # ds.filter{|o| o.<=(4, :a)} # SELECT * FROM t WHERE (4 <= a)
1568
+ #
1569
+ # # Literal Strings
1570
+ # ds.filter{{a=>`some SQL`}} # SELECT * FROM t WHERE (a = some SQL)
1571
+ #
1038
1572
  # For a more detailed explanation, see the {Virtual Rows guide}[link:files/doc/virtual_rows_rdoc.html].
1039
1573
  class VirtualRow < BasicObject
1040
1574
  WILDCARD = LiteralString.new('*').freeze
1041
1575
  QUESTION_MARK = LiteralString.new('?').freeze
1042
1576
  COMMA_SEPARATOR = LiteralString.new(', ').freeze
1043
1577
  DOUBLE_UNDERSCORE = '__'.freeze
1578
+ DISTINCT = ["DISTINCT ".freeze].freeze
1579
+ COMMA_ARRAY = [COMMA_SEPARATOR].freeze
1580
+
1581
+ include OperatorBuilders
1582
+
1583
+ %w'> < >= <='.each do |op|
1584
+ class_eval(<<-END, __FILE__, __LINE__ + 1)
1585
+ def #{op}(*args)
1586
+ SQL::BooleanExpression.new(:#{op}, *args)
1587
+ end
1588
+ END
1589
+ end
1590
+
1591
+ # Return a literal string created with the given string.
1592
+ def `(s)
1593
+ Sequel::LiteralString.new(s)
1594
+ end
1044
1595
 
1045
1596
  # Return an +Identifier+, +QualifiedIdentifier+, +Function+, or +WindowFunction+, depending
1046
1597
  # on arguments and whether a block is provided. Does not currently call the block.
@@ -1054,7 +1605,7 @@ module Sequel
1054
1605
  when :*
1055
1606
  Function.new(m, WILDCARD)
1056
1607
  when :distinct
1057
- Function.new(m, PlaceholderLiteralString.new("DISTINCT #{args.map{QUESTION_MARK}.join(COMMA_SEPARATOR)}", args))
1608
+ Function.new(m, PlaceholderLiteralString.new(DISTINCT + COMMA_ARRAY * (args.length-1), args))
1058
1609
  when :over
1059
1610
  opts = args.shift || {}
1060
1611
  fun_args = ::Kernel.Array(opts[:*] ? WILDCARD : opts[:args])
@@ -1108,6 +1659,20 @@ module Sequel
1108
1659
 
1109
1660
  to_s_method :window_function_sql, '@function, @window'
1110
1661
  end
1662
+
1663
+ # A +Wrapper+ is a simple way to wrap an existing object so that it supports
1664
+ # the Sequel DSL.
1665
+ class Wrapper < GenericExpression
1666
+ # The underlying value wrapped by this object.
1667
+ attr_reader :value
1668
+
1669
+ # Set the value wrapped by the object.
1670
+ def initialize(value)
1671
+ @value = value
1672
+ end
1673
+
1674
+ to_s_method :literal, '@value'
1675
+ end
1111
1676
  end
1112
1677
 
1113
1678
  # +LiteralString+ is used to represent literal SQL expressions. A
@@ -1120,7 +1685,22 @@ module Sequel
1120
1685
  include SQL::NumericMethods
1121
1686
  include SQL::StringMethods
1122
1687
  include SQL::InequalityMethods
1688
+ include SQL::AliasMethods
1689
+ include SQL::CastMethods
1690
+
1691
+ # Return self if no args are given, otherwise return a SQL::PlaceholderLiteralString
1692
+ # with the current string and the given args.
1693
+ def lit(*args)
1694
+ args.empty? ? self : SQL::PlaceholderLiteralString.new(self, args)
1695
+ end
1696
+
1697
+ # Convert a literal string to a SQL::Blob.
1698
+ def to_sequel_blob
1699
+ SQL::Blob.new(self)
1700
+ end
1123
1701
  end
1124
1702
 
1125
1703
  include SQL::Constants
1704
+ extend SQL::Builders
1705
+ extend SQL::OperatorBuilders
1126
1706
  end