sequel 3.21.0 → 3.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (352) hide show
  1. data/CHANGELOG +1061 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +103 -63
  4. data/Rakefile +59 -27
  5. data/bin/sequel +50 -26
  6. data/doc/active_record.rdoc +67 -51
  7. data/doc/advanced_associations.rdoc +189 -75
  8. data/doc/association_basics.rdoc +327 -79
  9. data/doc/cheat_sheet.rdoc +21 -21
  10. data/doc/core_extensions.rdoc +374 -0
  11. data/doc/dataset_basics.rdoc +10 -10
  12. data/doc/dataset_filtering.rdoc +54 -42
  13. data/doc/mass_assignment.rdoc +56 -0
  14. data/doc/migration.rdoc +89 -557
  15. data/doc/model_hooks.rdoc +80 -29
  16. data/doc/object_model.rdoc +547 -0
  17. data/doc/opening_databases.rdoc +114 -53
  18. data/doc/prepared_statements.rdoc +41 -15
  19. data/doc/querying.rdoc +137 -56
  20. data/doc/reflection.rdoc +36 -10
  21. data/doc/release_notes/3.22.0.txt +39 -0
  22. data/doc/release_notes/3.23.0.txt +172 -0
  23. data/doc/release_notes/3.24.0.txt +420 -0
  24. data/doc/release_notes/3.25.0.txt +88 -0
  25. data/doc/release_notes/3.26.0.txt +88 -0
  26. data/doc/release_notes/3.27.0.txt +82 -0
  27. data/doc/release_notes/3.28.0.txt +304 -0
  28. data/doc/release_notes/3.29.0.txt +459 -0
  29. data/doc/release_notes/3.30.0.txt +135 -0
  30. data/doc/release_notes/3.31.0.txt +146 -0
  31. data/doc/release_notes/3.32.0.txt +202 -0
  32. data/doc/release_notes/3.33.0.txt +157 -0
  33. data/doc/release_notes/3.34.0.txt +671 -0
  34. data/doc/release_notes/3.35.0.txt +144 -0
  35. data/doc/release_notes/3.36.0.txt +245 -0
  36. data/doc/release_notes/3.37.0.txt +338 -0
  37. data/doc/release_notes/3.38.0.txt +234 -0
  38. data/doc/release_notes/3.39.0.txt +237 -0
  39. data/doc/schema_modification.rdoc +585 -0
  40. data/doc/sharding.rdoc +99 -8
  41. data/doc/sql.rdoc +154 -112
  42. data/doc/testing.rdoc +169 -0
  43. data/doc/thread_safety.rdoc +17 -0
  44. data/doc/transactions.rdoc +137 -0
  45. data/doc/validations.rdoc +1 -1
  46. data/doc/virtual_rows.rdoc +78 -43
  47. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  48. data/lib/sequel/adapters/ado.rb +40 -18
  49. data/lib/sequel/adapters/amalgalite.rb +17 -9
  50. data/lib/sequel/adapters/db2.rb +175 -86
  51. data/lib/sequel/adapters/dbi.rb +15 -15
  52. data/lib/sequel/adapters/do/mysql.rb +17 -11
  53. data/lib/sequel/adapters/do/postgres.rb +2 -61
  54. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  55. data/lib/sequel/adapters/do.rb +18 -36
  56. data/lib/sequel/adapters/firebird.rb +27 -208
  57. data/lib/sequel/adapters/ibmdb.rb +453 -0
  58. data/lib/sequel/adapters/informix.rb +6 -23
  59. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  60. data/lib/sequel/adapters/jdbc/db2.rb +56 -0
  61. data/lib/sequel/adapters/jdbc/derby.rb +325 -0
  62. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  63. data/lib/sequel/adapters/jdbc/h2.rb +73 -31
  64. data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
  65. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  66. data/lib/sequel/adapters/jdbc/jtds.rb +40 -0
  67. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  68. data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
  69. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  70. data/lib/sequel/adapters/jdbc/postgresql.rb +77 -52
  71. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  72. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  73. data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
  74. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  75. data/lib/sequel/adapters/jdbc.rb +279 -97
  76. data/lib/sequel/adapters/mock.rb +372 -0
  77. data/lib/sequel/adapters/mysql.rb +125 -222
  78. data/lib/sequel/adapters/mysql2.rb +65 -56
  79. data/lib/sequel/adapters/odbc/mssql.rb +17 -9
  80. data/lib/sequel/adapters/odbc.rb +28 -9
  81. data/lib/sequel/adapters/openbase.rb +3 -5
  82. data/lib/sequel/adapters/oracle.rb +349 -53
  83. data/lib/sequel/adapters/postgres.rb +368 -138
  84. data/lib/sequel/adapters/shared/access.rb +32 -9
  85. data/lib/sequel/adapters/shared/db2.rb +343 -0
  86. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  87. data/lib/sequel/adapters/shared/informix.rb +53 -0
  88. data/lib/sequel/adapters/shared/mssql.rb +401 -116
  89. data/lib/sequel/adapters/shared/mysql.rb +424 -89
  90. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
  91. data/lib/sequel/adapters/shared/oracle.rb +257 -45
  92. data/lib/sequel/adapters/shared/postgres.rb +675 -279
  93. data/lib/sequel/adapters/shared/progress.rb +5 -8
  94. data/lib/sequel/adapters/shared/sqlite.rb +285 -96
  95. data/lib/sequel/adapters/sqlite.rb +101 -39
  96. data/lib/sequel/adapters/swift/mysql.rb +10 -12
  97. data/lib/sequel/adapters/swift/postgres.rb +13 -79
  98. data/lib/sequel/adapters/swift/sqlite.rb +9 -6
  99. data/lib/sequel/adapters/swift.rb +28 -19
  100. data/lib/sequel/adapters/tinytds.rb +174 -22
  101. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +77 -0
  102. data/lib/sequel/adapters/utils/pg_types.rb +81 -0
  103. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  104. data/lib/sequel/ast_transformer.rb +194 -0
  105. data/lib/sequel/connection_pool/sharded_single.rb +11 -1
  106. data/lib/sequel/connection_pool/sharded_threaded.rb +33 -2
  107. data/lib/sequel/connection_pool/single.rb +5 -0
  108. data/lib/sequel/connection_pool/threaded.rb +43 -14
  109. data/lib/sequel/connection_pool.rb +10 -2
  110. data/lib/sequel/core.rb +143 -11
  111. data/lib/sequel/database/connecting.rb +38 -28
  112. data/lib/sequel/database/dataset.rb +4 -4
  113. data/lib/sequel/database/dataset_defaults.rb +63 -2
  114. data/lib/sequel/database/logging.rb +7 -2
  115. data/lib/sequel/database/misc.rb +190 -46
  116. data/lib/sequel/database/query.rb +272 -110
  117. data/lib/sequel/database/schema_generator.rb +103 -36
  118. data/lib/sequel/database/schema_methods.rb +300 -67
  119. data/lib/sequel/dataset/actions.rb +344 -90
  120. data/lib/sequel/dataset/features.rb +101 -8
  121. data/lib/sequel/dataset/graph.rb +42 -15
  122. data/lib/sequel/dataset/misc.rb +52 -25
  123. data/lib/sequel/dataset/mutation.rb +12 -7
  124. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  125. data/lib/sequel/dataset/query.rb +305 -54
  126. data/lib/sequel/dataset/sql.rb +720 -292
  127. data/lib/sequel/dataset.rb +8 -0
  128. data/lib/sequel/exceptions.rb +4 -0
  129. data/lib/sequel/extensions/_pretty_table.rb +83 -0
  130. data/lib/sequel/extensions/arbitrary_servers.rb +109 -0
  131. data/lib/sequel/extensions/blank.rb +4 -0
  132. data/lib/sequel/extensions/columns_introspection.rb +72 -0
  133. data/lib/sequel/extensions/constraint_validations.rb +451 -0
  134. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +30 -46
  135. data/lib/sequel/extensions/eval_inspect.rb +173 -0
  136. data/lib/sequel/extensions/inflector.rb +4 -0
  137. data/lib/sequel/extensions/looser_typecasting.rb +5 -4
  138. data/lib/sequel/extensions/migration.rb +100 -11
  139. data/lib/sequel/extensions/named_timezones.rb +9 -0
  140. data/lib/sequel/extensions/null_dataset.rb +94 -0
  141. data/lib/sequel/extensions/pagination.rb +4 -0
  142. data/lib/sequel/extensions/pg_array.rb +537 -0
  143. data/lib/sequel/extensions/pg_array_ops.rb +263 -0
  144. data/lib/sequel/extensions/pg_auto_parameterize.rb +175 -0
  145. data/lib/sequel/extensions/pg_hstore.rb +313 -0
  146. data/lib/sequel/extensions/pg_hstore_ops.rb +293 -0
  147. data/lib/sequel/extensions/pg_inet.rb +113 -0
  148. data/lib/sequel/extensions/pg_interval.rb +191 -0
  149. data/lib/sequel/extensions/pg_json.rb +208 -0
  150. data/lib/sequel/extensions/pg_range.rb +512 -0
  151. data/lib/sequel/extensions/pg_range_ops.rb +150 -0
  152. data/lib/sequel/extensions/pg_row.rb +572 -0
  153. data/lib/sequel/extensions/pg_row_ops.rb +182 -0
  154. data/lib/sequel/extensions/pg_statement_cache.rb +317 -0
  155. data/lib/sequel/extensions/pretty_table.rb +17 -72
  156. data/lib/sequel/extensions/query.rb +8 -4
  157. data/lib/sequel/extensions/query_literals.rb +79 -0
  158. data/lib/sequel/extensions/schema_caching.rb +76 -0
  159. data/lib/sequel/extensions/schema_dumper.rb +282 -76
  160. data/lib/sequel/extensions/select_remove.rb +39 -0
  161. data/lib/sequel/extensions/server_block.rb +140 -0
  162. data/lib/sequel/extensions/split_array_nil.rb +65 -0
  163. data/lib/sequel/extensions/sql_expr.rb +8 -110
  164. data/lib/sequel/extensions/string_date_time.rb +4 -0
  165. data/lib/sequel/extensions/thread_local_timezones.rb +10 -4
  166. data/lib/sequel/extensions/to_dot.rb +99 -83
  167. data/lib/sequel/model/associations.rb +1263 -409
  168. data/lib/sequel/model/base.rb +624 -171
  169. data/lib/sequel/model/errors.rb +1 -1
  170. data/lib/sequel/model/exceptions.rb +24 -2
  171. data/lib/sequel/model/inflections.rb +1 -1
  172. data/lib/sequel/model.rb +30 -11
  173. data/lib/sequel/no_core_ext.rb +2 -0
  174. data/lib/sequel/plugins/active_model.rb +13 -1
  175. data/lib/sequel/plugins/association_pks.rb +22 -4
  176. data/lib/sequel/plugins/caching.rb +25 -18
  177. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  178. data/lib/sequel/plugins/composition.rb +44 -12
  179. data/lib/sequel/plugins/constraint_validations.rb +198 -0
  180. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  181. data/lib/sequel/plugins/defaults_setter.rb +72 -0
  182. data/lib/sequel/plugins/dirty.rb +214 -0
  183. data/lib/sequel/plugins/eager_each.rb +59 -0
  184. data/lib/sequel/plugins/force_encoding.rb +6 -6
  185. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  186. data/lib/sequel/plugins/identity_map.rb +134 -15
  187. data/lib/sequel/plugins/instance_filters.rb +10 -0
  188. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  189. data/lib/sequel/plugins/json_serializer.rb +58 -6
  190. data/lib/sequel/plugins/list.rb +13 -2
  191. data/lib/sequel/plugins/many_through_many.rb +103 -51
  192. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  193. data/lib/sequel/plugins/nested_attributes.rb +150 -66
  194. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  195. data/lib/sequel/plugins/pg_row.rb +121 -0
  196. data/lib/sequel/plugins/pg_typecast_on_load.rb +65 -0
  197. data/lib/sequel/plugins/prepared_statements.rb +167 -0
  198. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  199. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  200. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  201. data/lib/sequel/plugins/rcte_tree.rb +31 -17
  202. data/lib/sequel/plugins/schema.rb +8 -3
  203. data/lib/sequel/plugins/serialization.rb +98 -49
  204. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  205. data/lib/sequel/plugins/sharding.rb +21 -54
  206. data/lib/sequel/plugins/single_table_inheritance.rb +5 -3
  207. data/lib/sequel/plugins/static_cache.rb +99 -0
  208. data/lib/sequel/plugins/subclasses.rb +29 -3
  209. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  210. data/lib/sequel/plugins/timestamps.rb +1 -1
  211. data/lib/sequel/plugins/tree.rb +3 -3
  212. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  213. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  214. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  215. data/lib/sequel/plugins/validation_helpers.rb +55 -4
  216. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  217. data/lib/sequel/sql.rb +649 -122
  218. data/lib/sequel/timezones.rb +67 -40
  219. data/lib/sequel/version.rb +1 -1
  220. data/spec/adapters/db2_spec.rb +146 -0
  221. data/spec/adapters/firebird_spec.rb +1 -1
  222. data/spec/adapters/mssql_spec.rb +194 -66
  223. data/spec/adapters/mysql_spec.rb +475 -306
  224. data/spec/adapters/oracle_spec.rb +92 -117
  225. data/spec/adapters/postgres_spec.rb +1982 -325
  226. data/spec/adapters/spec_helper.rb +9 -6
  227. data/spec/adapters/sqlite_spec.rb +203 -75
  228. data/spec/core/connection_pool_spec.rb +218 -93
  229. data/spec/core/database_spec.rb +956 -522
  230. data/spec/core/dataset_spec.rb +1631 -1242
  231. data/spec/core/expression_filters_spec.rb +690 -334
  232. data/spec/core/mock_adapter_spec.rb +453 -0
  233. data/spec/core/object_graph_spec.rb +88 -129
  234. data/spec/core/schema_generator_spec.rb +4 -4
  235. data/spec/core/schema_spec.rb +556 -53
  236. data/spec/core/spec_helper.rb +7 -48
  237. data/spec/core_extensions_spec.rb +626 -0
  238. data/spec/extensions/active_model_spec.rb +13 -0
  239. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  240. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  241. data/spec/extensions/association_dependencies_spec.rb +15 -25
  242. data/spec/extensions/association_pks_spec.rb +66 -32
  243. data/spec/extensions/association_proxies_spec.rb +4 -4
  244. data/spec/extensions/boolean_readers_spec.rb +25 -25
  245. data/spec/extensions/caching_spec.rb +47 -51
  246. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  247. data/spec/extensions/columns_introspection_spec.rb +91 -0
  248. data/spec/extensions/composition_spec.rb +18 -13
  249. data/spec/extensions/constraint_validations_plugin_spec.rb +196 -0
  250. data/spec/extensions/constraint_validations_spec.rb +316 -0
  251. data/spec/extensions/dataset_associations_spec.rb +199 -0
  252. data/spec/extensions/defaults_setter_spec.rb +88 -0
  253. data/spec/extensions/dirty_spec.rb +155 -0
  254. data/spec/extensions/eager_each_spec.rb +34 -0
  255. data/spec/extensions/eval_inspect_spec.rb +67 -0
  256. data/spec/extensions/force_encoding_spec.rb +4 -2
  257. data/spec/extensions/hook_class_methods_spec.rb +97 -128
  258. data/spec/extensions/identity_map_spec.rb +142 -24
  259. data/spec/extensions/inflector_spec.rb +0 -4
  260. data/spec/extensions/instance_filters_spec.rb +11 -21
  261. data/spec/extensions/instance_hooks_spec.rb +72 -0
  262. data/spec/extensions/json_serializer_spec.rb +49 -12
  263. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  264. data/spec/extensions/list_spec.rb +49 -40
  265. data/spec/extensions/looser_typecasting_spec.rb +7 -7
  266. data/spec/extensions/many_through_many_spec.rb +368 -254
  267. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  268. data/spec/extensions/migration_spec.rb +140 -35
  269. data/spec/extensions/named_timezones_spec.rb +29 -11
  270. data/spec/extensions/nested_attributes_spec.rb +268 -89
  271. data/spec/extensions/null_dataset_spec.rb +85 -0
  272. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  273. data/spec/extensions/pg_array_ops_spec.rb +112 -0
  274. data/spec/extensions/pg_array_spec.rb +320 -0
  275. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  276. data/spec/extensions/pg_hstore_ops_spec.rb +140 -0
  277. data/spec/extensions/pg_hstore_spec.rb +194 -0
  278. data/spec/extensions/pg_inet_spec.rb +47 -0
  279. data/spec/extensions/pg_interval_spec.rb +72 -0
  280. data/spec/extensions/pg_json_spec.rb +99 -0
  281. data/spec/extensions/pg_range_ops_spec.rb +56 -0
  282. data/spec/extensions/pg_range_spec.rb +395 -0
  283. data/spec/extensions/pg_row_ops_spec.rb +58 -0
  284. data/spec/extensions/pg_row_plugin_spec.rb +49 -0
  285. data/spec/extensions/pg_row_spec.rb +323 -0
  286. data/spec/extensions/pg_statement_cache_spec.rb +208 -0
  287. data/spec/extensions/pg_typecast_on_load_spec.rb +58 -0
  288. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  289. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  290. data/spec/extensions/prepared_statements_spec.rb +87 -0
  291. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  292. data/spec/extensions/pretty_table_spec.rb +7 -6
  293. data/spec/extensions/query_literals_spec.rb +167 -0
  294. data/spec/extensions/query_spec.rb +9 -3
  295. data/spec/extensions/rcte_tree_spec.rb +50 -43
  296. data/spec/extensions/schema_caching_spec.rb +41 -0
  297. data/spec/extensions/schema_dumper_spec.rb +434 -49
  298. data/spec/extensions/schema_spec.rb +30 -49
  299. data/spec/extensions/select_remove_spec.rb +38 -0
  300. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  301. data/spec/extensions/serialization_spec.rb +86 -48
  302. data/spec/extensions/server_block_spec.rb +90 -0
  303. data/spec/extensions/sharding_spec.rb +69 -143
  304. data/spec/extensions/single_table_inheritance_spec.rb +23 -29
  305. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  306. data/spec/extensions/spec_helper.rb +34 -67
  307. data/spec/extensions/split_array_nil_spec.rb +24 -0
  308. data/spec/extensions/sql_expr_spec.rb +29 -60
  309. data/spec/extensions/static_cache_spec.rb +145 -0
  310. data/spec/extensions/subclasses_spec.rb +14 -0
  311. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  312. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  313. data/spec/extensions/timestamps_spec.rb +6 -6
  314. data/spec/extensions/to_dot_spec.rb +8 -11
  315. data/spec/extensions/touch_spec.rb +13 -14
  316. data/spec/extensions/tree_spec.rb +11 -26
  317. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  318. data/spec/extensions/update_primary_key_spec.rb +30 -24
  319. data/spec/extensions/validation_class_methods_spec.rb +58 -67
  320. data/spec/extensions/validation_helpers_spec.rb +31 -37
  321. data/spec/extensions/xml_serializer_spec.rb +29 -4
  322. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  323. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  324. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  325. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  326. data/spec/integration/associations_test.rb +659 -21
  327. data/spec/integration/database_test.rb +11 -5
  328. data/spec/integration/dataset_test.rb +717 -129
  329. data/spec/integration/eager_loader_test.rb +38 -54
  330. data/spec/integration/migrator_test.rb +2 -3
  331. data/spec/integration/model_test.rb +81 -2
  332. data/spec/integration/plugin_test.rb +604 -147
  333. data/spec/integration/prepared_statement_test.rb +272 -128
  334. data/spec/integration/schema_test.rb +367 -57
  335. data/spec/integration/spec_helper.rb +42 -32
  336. data/spec/integration/timezone_test.rb +38 -12
  337. data/spec/integration/transaction_test.rb +183 -8
  338. data/spec/integration/type_test.rb +31 -8
  339. data/spec/model/association_reflection_spec.rb +207 -8
  340. data/spec/model/associations_spec.rb +1206 -693
  341. data/spec/model/base_spec.rb +334 -100
  342. data/spec/model/dataset_methods_spec.rb +45 -27
  343. data/spec/model/eager_loading_spec.rb +828 -692
  344. data/spec/model/hooks_spec.rb +355 -67
  345. data/spec/model/model_spec.rb +251 -179
  346. data/spec/model/plugins_spec.rb +24 -13
  347. data/spec/model/record_spec.rb +506 -232
  348. data/spec/model/spec_helper.rb +16 -77
  349. data/spec/model/validations_spec.rb +38 -2
  350. data/spec/rcov.opts +2 -0
  351. metadata +209 -80
  352. data/spec/core/core_sql_spec.rb +0 -451
@@ -1,10 +1,15 @@
1
1
  module Sequel
2
2
  class Dataset
3
3
  # ---------------------
4
- # :section: Methods that return modified datasets
4
+ # :section: 1 - Methods that return modified datasets
5
5
  # These methods all return modified copies of the receiver.
6
6
  # ---------------------
7
7
 
8
+ # Hash of extension name symbols to callable objects to load the extension
9
+ # into the Dataset object (usually by extending it with a module defined
10
+ # in the extension).
11
+ EXTENSIONS = {}
12
+
8
13
  # The dataset options that require the removal of cached columns
9
14
  # if changed.
10
15
  COLUMN_CHANGE_OPTS = [:select, :sql, :from, :join].freeze
@@ -28,12 +33,35 @@ module Sequel
28
33
  JOIN_METHODS = (CONDITIONED_JOIN_TYPES + UNCONDITIONED_JOIN_TYPES).map{|x| "#{x}_join".to_sym} + [:join, :join_table]
29
34
 
30
35
  # Methods that return modified datasets
31
- QUERY_METHODS = %w'add_graph_aliases and distinct except exclude
32
- filter for_update from from_self graph grep group group_and_count group_by having intersect invert
33
- limit lock_style naked or order order_append order_by order_more order_prepend paginate qualify query
34
- reverse reverse_order select select_all select_append select_more server
35
- set_defaults set_graph_aliases set_overrides unfiltered ungraphed ungrouped union
36
- unlimited unordered where with with_recursive with_sql'.collect{|x| x.to_sym} + JOIN_METHODS
36
+ QUERY_METHODS = (<<-METHS).split.map{|x| x.to_sym} + JOIN_METHODS
37
+ add_graph_aliases and distinct except exclude exclude_having exclude_where
38
+ filter for_update from from_self graph grep group group_and_count group_by having intersect invert
39
+ limit lock_style naked or order order_append order_by order_more order_prepend paginate qualify query
40
+ reverse reverse_order select select_all select_append select_group select_more server
41
+ set_defaults set_graph_aliases set_overrides unfiltered ungraphed ungrouped union
42
+ unlimited unordered where with with_recursive with_sql
43
+ METHS
44
+
45
+ # Register an extension callback for Dataset objects. ext should be the
46
+ # extension name symbol, and mod should either be a Module that the
47
+ # dataset is extended with, or a callable object called with the database
48
+ # object. If mod is not provided, a block can be provided and is treated
49
+ # as the mod object.
50
+ #
51
+ # If mod is a module, this also registers a Database extension that will
52
+ # extend all of the database's datasets.
53
+ def self.register_extension(ext, mod=nil, &block)
54
+ if mod
55
+ raise(Error, "cannot provide both mod and block to Dataset.register_extension") if block
56
+ if mod.is_a?(Module)
57
+ block = proc{|ds| ds.extend(mod)}
58
+ Sequel::Database.register_extension(ext){|db| db.extend_datasets(mod)}
59
+ else
60
+ block = mod
61
+ end
62
+ end
63
+ Sequel.synchronize{EXTENSIONS[ext] = block}
64
+ end
37
65
 
38
66
  # Adds an further filter to an existing filter using AND. If no filter
39
67
  # exists an error is raised. This method is identical to #filter except
@@ -80,7 +108,7 @@ module Sequel
80
108
  # :from_self :: Set to false to not wrap the returned dataset in a from_self, use with care.
81
109
  #
82
110
  # DB[:items].except(DB[:other_items])
83
- # # SELECT * FROM items EXCEPT SELECT * FROM other_items
111
+ # # SELECT * FROM (SELECT * FROM items EXCEPT SELECT * FROM other_items) AS t1
84
112
  #
85
113
  # DB[:items].except(DB[:other_items], :all=>true, :from_self=>false)
86
114
  # # SELECT * FROM items EXCEPT ALL SELECT * FROM other_items
@@ -103,12 +131,51 @@ module Sequel
103
131
  # DB[:items].exclude(:category => 'software', :id=>3)
104
132
  # # SELECT * FROM items WHERE ((category != 'software') OR (id != 3))
105
133
  def exclude(*cond, &block)
106
- clause = (@opts[:having] ? :having : :where)
107
- cond = cond.first if cond.size == 1
108
- cond = filter_expr(cond, &block)
109
- cond = SQL::BooleanExpression.invert(cond)
110
- cond = SQL::BooleanExpression.new(:AND, @opts[clause], cond) if @opts[clause]
111
- clone(clause => cond)
134
+ _filter_or_exclude(true, @opts[:having] ? :having : :where, *cond, &block)
135
+ end
136
+
137
+ # Inverts the given conditions and adds them to the HAVING clause.
138
+ #
139
+ # DB[:items].select_group(:name).exclude_having{count(name) < 2}
140
+ # # SELECT name FROM items GROUP BY name HAVING (count(name) >= 2)
141
+ def exclude_having(*cond, &block)
142
+ _filter_or_exclude(true, :having, *cond, &block)
143
+ end
144
+
145
+ # Inverts the given conditions and adds them to the WHERE clause.
146
+ #
147
+ # DB[:items].select_group(:name).exclude_where(:category => 'software')
148
+ # # SELECT * FROM items WHERE (category != 'software')
149
+ #
150
+ # DB[:items].select_group(:name).
151
+ # exclude_having{count(name) < 2}.
152
+ # exclude_where(:category => 'software')
153
+ # # SELECT name FROM items WHERE (category != 'software')
154
+ # # GROUP BY name HAVING (count(name) >= 2)
155
+ def exclude_where(*cond, &block)
156
+ _filter_or_exclude(true, :where, *cond, &block)
157
+ end
158
+
159
+ # Return a clone of the dataset loaded with the extensions, see #extension!.
160
+ def extension(*exts)
161
+ clone.extension!(*exts)
162
+ end
163
+
164
+ # Load an extension into the receiver. In addition to requiring the extension file, this
165
+ # also modifies the dataset to work with the extension (usually extending it with a
166
+ # module defined in the extension file). If no related extension file exists or the
167
+ # extension does not have specific support for Database objects, an Error will be raised.
168
+ # Returns self.
169
+ def extension!(*exts)
170
+ Sequel.extension(*exts)
171
+ exts.each do |ext|
172
+ if pr = Sequel.synchronize{EXTENSIONS[ext]}
173
+ pr.call(self)
174
+ else
175
+ raise(Error, "Extension #{ext} does not have specific support handling individual datasets")
176
+ end
177
+ end
178
+ self
112
179
  end
113
180
 
114
181
  # Returns a copy of the dataset with the given conditions imposed upon it.
@@ -145,7 +212,7 @@ module Sequel
145
212
  # DB[:items].filter('price < ?', 100)
146
213
  # # SELECT * FROM items WHERE price < 100
147
214
  #
148
- # DB[:items].filter([[:id, (1,2,3)], [:id, 0..10]])
215
+ # DB[:items].filter([[:id, [1,2,3]], [:id, 0..10]])
149
216
  # # SELECT * FROM items WHERE ((id IN (1, 2, 3)) AND ((id >= 0) AND (id <= 10)))
150
217
  #
151
218
  # DB[:items].filter('price < 100')
@@ -184,16 +251,22 @@ module Sequel
184
251
  def from(*source)
185
252
  table_alias_num = 0
186
253
  sources = []
254
+ ctes = nil
187
255
  source.each do |s|
188
256
  case s
189
257
  when Hash
190
258
  s.each{|k,v| sources << SQL::AliasedExpression.new(k,v)}
191
259
  when Dataset
260
+ if hoist_cte?(s)
261
+ ctes ||= []
262
+ ctes += s.opts[:with]
263
+ s = s.clone(:with=>nil)
264
+ end
192
265
  sources << SQL::AliasedExpression.new(s, dataset_alias(table_alias_num+=1))
193
266
  when Symbol
194
267
  sch, table, aliaz = split_symbol(s)
195
268
  if aliaz
196
- s = sch ? SQL::QualifiedIdentifier.new(sch.to_sym, table.to_sym) : SQL::Identifier.new(table.to_sym)
269
+ s = sch ? SQL::QualifiedIdentifier.new(sch, table) : SQL::Identifier.new(table)
197
270
  sources << SQL::AliasedExpression.new(s, aliaz.to_sym)
198
271
  else
199
272
  sources << s
@@ -203,6 +276,7 @@ module Sequel
203
276
  end
204
277
  end
205
278
  o = {:from=>sources.empty? ? nil : sources}
279
+ o[:with] = (opts[:with] || []) + ctes if ctes
206
280
  o[:num_dataset_sources] = table_alias_num if table_alias_num > 0
207
281
  clone(o)
208
282
  end
@@ -270,21 +344,25 @@ module Sequel
270
344
  end
271
345
 
272
346
  # Returns a copy of the dataset with the results grouped by the value of
273
- # the given columns.
347
+ # the given columns. If a block is given, it is treated
348
+ # as a virtual row block, similar to +filter+.
274
349
  #
275
350
  # DB[:items].group(:id) # SELECT * FROM items GROUP BY id
276
351
  # DB[:items].group(:id, :name) # SELECT * FROM items GROUP BY id, name
277
- def group(*columns)
352
+ # DB[:items].group{[a, sum(b)]} # SELECT * FROM items GROUP BY a, sum(b)
353
+ def group(*columns, &block)
354
+ virtual_row_columns(columns, block)
278
355
  clone(:group => (columns.compact.empty? ? nil : columns))
279
356
  end
280
357
 
281
358
  # Alias of group
282
- def group_by(*columns)
283
- group(*columns)
359
+ def group_by(*columns, &block)
360
+ group(*columns, &block)
284
361
  end
285
362
 
286
363
  # Returns a dataset grouped by the given column with count by group.
287
364
  # Column aliases may be supplied, and will be included in the select clause.
365
+ # If a block is given, it is treated as a virtual row block, similar to +filter+.
288
366
  #
289
367
  # Examples:
290
368
  #
@@ -299,8 +377,24 @@ module Sequel
299
377
  # DB[:items].group_and_count(:first_name___name).all
300
378
  # # SELECT first_name AS name, count(*) AS count FROM items GROUP BY first_name
301
379
  # # => [{:name=>'a', :count=>1}, ...]
302
- def group_and_count(*columns)
303
- group(*columns.map{|c| unaliased_identifier(c)}).select(*(columns + [COUNT_OF_ALL_AS_COUNT]))
380
+ #
381
+ # DB[:items].group_and_count{substr(first_name, 1, 1).as(initial)}.all
382
+ # # SELECT substr(first_name, 1, 1) AS initial, count(*) AS count FROM items GROUP BY substr(first_name, 1, 1)
383
+ # # => [{:initial=>'a', :count=>1}, ...]
384
+ def group_and_count(*columns, &block)
385
+ select_group(*columns, &block).select_more(COUNT_OF_ALL_AS_COUNT)
386
+ end
387
+
388
+ # Adds the appropriate CUBE syntax to GROUP BY.
389
+ def group_cube
390
+ raise Error, "GROUP BY CUBE not supported on #{db.database_type}" unless supports_group_cube?
391
+ clone(:group_options=>:cube)
392
+ end
393
+
394
+ # Adds the appropriate ROLLUP syntax to GROUP BY.
395
+ def group_rollup
396
+ raise Error, "GROUP BY ROLLUP not supported on #{db.database_type}" unless supports_group_rollup?
397
+ clone(:group_options=>:rollup)
304
398
  end
305
399
 
306
400
  # Returns a copy of the dataset with the HAVING conditions changed. See #filter for argument types.
@@ -356,12 +450,15 @@ module Sequel
356
450
  inner_join(*args, &block)
357
451
  end
358
452
 
359
- # Returns a joined dataset. Uses the following arguments:
453
+ # Returns a joined dataset. Not usually called directly, users should use the
454
+ # appropriate join method (e.g. join, left_join, natural_join, cross_join) which fills
455
+ # in the +type+ argument.
456
+ #
457
+ # Takes the following arguments:
360
458
  #
361
459
  # * type - The type of join to do (e.g. :inner)
362
460
  # * table - Depends on type:
363
461
  # * Dataset - a subselect is performed with an alias of tN for some value of N
364
- # * Model (or anything responding to :table_name) - table.table_name
365
462
  # * String, Symbol: table
366
463
  # * expr - specifies conditions, depends on type:
367
464
  # * Hash, Array of two element arrays - Assumes key (1st arg) is column of joined table (unless already
@@ -382,11 +479,36 @@ module Sequel
382
479
  # to the same table more than once. No alias is used by default.
383
480
  # * :implicit_qualifier - The name to use for qualifying implicit conditions. By default,
384
481
  # the last joined or primary table is used.
482
+ # * :qualify - Can be set to false to not do any implicit qualification. Can be set
483
+ # to :deep to use the Qualifier AST Transformer, which will attempt to qualify
484
+ # subexpressions of the expression tree.
385
485
  # * block - The block argument should only be given if a JOIN with an ON clause is used,
386
486
  # in which case it yields the table alias/name for the table currently being joined,
387
487
  # the table alias/name for the last joined (or first table), and an array of previous
388
488
  # SQL::JoinClause. Unlike +filter+, this block is not treated as a virtual row block.
489
+ #
490
+ # Examples:
491
+ #
492
+ # DB[:a].join_table(:cross, :b)
493
+ # # SELECT * FROM a CROSS JOIN b
494
+ #
495
+ # DB[:a].join_table(:inner, DB[:b], :c=>d)
496
+ # # SELECT * FROM a INNER JOIN (SELECT * FROM b) AS t1 ON (t1.c = a.d)
497
+ #
498
+ # DB[:a].join_table(:left, :b___c, [:d])
499
+ # # SELECT * FROM a LEFT JOIN b AS c USING (d)
500
+ #
501
+ # DB[:a].natural_join(:b).join_table(:inner, :c) do |ta, jta, js|
502
+ # (Sequel.qualify(ta, :d) > Sequel.qualify(jta, :e)) & {Sequel.qualify(ta, :f)=>DB.from(js.first.table).select(:g)}
503
+ # end
504
+ # # SELECT * FROM a NATURAL JOIN b INNER JOIN c
505
+ # # ON ((c.d > b.e) AND (c.f IN (SELECT g FROM b)))
389
506
  def join_table(type, table, expr=nil, options={}, &block)
507
+ if hoist_cte?(table)
508
+ s, ds = hoist_cte(table)
509
+ return s.join_table(type, ds, expr, options, &block)
510
+ end
511
+
390
512
  using_join = expr.is_a?(Array) && !expr.empty? && expr.all?{|x| x.is_a?(Symbol)}
391
513
  if using_join && !supports_join_using?
392
514
  h = {}
@@ -398,6 +520,7 @@ module Sequel
398
520
  when Hash
399
521
  table_alias = options[:table_alias]
400
522
  last_alias = options[:implicit_qualifier]
523
+ qualify_type = options[:qualify]
401
524
  when Symbol, String, SQL::Identifier
402
525
  table_alias = options
403
526
  last_alias = nil
@@ -412,7 +535,6 @@ module Sequel
412
535
  end
413
536
  table_name = table_alias
414
537
  else
415
- table = table.table_name if table.respond_to?(:table_name)
416
538
  table, implicit_table_alias = split_alias(table)
417
539
  table_alias ||= implicit_table_alias
418
540
  table_name = table_alias || table
@@ -427,10 +549,19 @@ module Sequel
427
549
  last_alias ||= @opts[:last_joined_table] || first_source_alias
428
550
  if Sequel.condition_specifier?(expr)
429
551
  expr = expr.collect do |k, v|
430
- k = qualified_column_name(k, table_name) if k.is_a?(Symbol)
431
- v = qualified_column_name(v, last_alias) if v.is_a?(Symbol)
552
+ case qualify_type
553
+ when false
554
+ nil # Do no qualification
555
+ when :deep
556
+ k = Sequel::Qualifier.new(self, table_name).transform(k)
557
+ v = Sequel::Qualifier.new(self, last_alias).transform(v)
558
+ else
559
+ k = qualified_column_name(k, table_name) if k.is_a?(Symbol)
560
+ v = qualified_column_name(v, last_alias) if v.is_a?(Symbol)
561
+ end
432
562
  [k,v]
433
563
  end
564
+ expr = SQL::BooleanExpression.from_value_pairs(expr)
434
565
  end
435
566
  if block
436
567
  expr2 = yield(table_name, last_alias, @opts[:join] || [])
@@ -524,14 +655,14 @@ module Sequel
524
655
  #
525
656
  # DB[:items].order(:name) # SELECT * FROM items ORDER BY name
526
657
  # DB[:items].order(:a, :b) # SELECT * FROM items ORDER BY a, b
527
- # DB[:items].order('a + b'.lit) # SELECT * FROM items ORDER BY a + b
658
+ # DB[:items].order(Sequel.lit('a + b')) # SELECT * FROM items ORDER BY a + b
528
659
  # DB[:items].order(:a + :b) # SELECT * FROM items ORDER BY (a + b)
529
- # DB[:items].order(:name.desc) # SELECT * FROM items ORDER BY name DESC
530
- # DB[:items].order(:name.asc(:nulls=>:last)) # SELECT * FROM items ORDER BY name ASC NULLS LAST
660
+ # DB[:items].order(Sequel.desc(:name)) # SELECT * FROM items ORDER BY name DESC
661
+ # DB[:items].order(Sequel.asc(:name, :nulls=>:last)) # SELECT * FROM items ORDER BY name ASC NULLS LAST
531
662
  # DB[:items].order{sum(name).desc} # SELECT * FROM items ORDER BY sum(name) DESC
532
663
  # DB[:items].order(nil) # SELECT * FROM items
533
664
  def order(*columns, &block)
534
- columns += Array(Sequel.virtual_row(&block)) if block
665
+ virtual_row_columns(columns, block)
535
666
  clone(:order => (columns.compact.empty?) ? nil : columns)
536
667
  end
537
668
 
@@ -565,7 +696,7 @@ module Sequel
565
696
  @opts[:order] ? ds.order_more(*@opts[:order]) : ds
566
697
  end
567
698
 
568
- # Qualify to the given table, or first source if not table is given.
699
+ # Qualify to the given table, or first source if no table is given.
569
700
  #
570
701
  # DB[:items].filter(:id=>1).qualify
571
702
  # # SELECT items.* FROM items WHERE (items.id = 1)
@@ -606,12 +737,24 @@ module Sequel
606
737
  qualify_to(first_source)
607
738
  end
608
739
 
740
+ # Modify the RETURNING clause, only supported on a few databases. If returning
741
+ # is used, instead of insert returning the autogenerated primary key or
742
+ # update/delete returning the number of modified rows, results are
743
+ # returned using +fetch_rows+.
744
+ #
745
+ # DB[:items].returning # RETURNING *
746
+ # DB[:items].returning(nil) # RETURNING NULL
747
+ # DB[:items].returning(:id, :name) # RETURNING id, name
748
+ def returning(*values)
749
+ clone(:returning=>values)
750
+ end
751
+
609
752
  # Returns a copy of the dataset with the order reversed. If no order is
610
753
  # given, the existing order is inverted.
611
754
  #
612
755
  # DB[:items].reverse(:id) # SELECT * FROM items ORDER BY id DESC
613
756
  # DB[:items].order(:id).reverse # SELECT * FROM items ORDER BY id DESC
614
- # DB[:items].order(:id).reverse(:name.asc) # SELECT * FROM items ORDER BY name ASC
757
+ # DB[:items].order(:id).reverse(Sequel.desc(:name)) # SELECT * FROM items ORDER BY name ASC
615
758
  def reverse(*order)
616
759
  order(*invert_order(order.empty? ? @opts[:order] : order))
617
760
  end
@@ -629,7 +772,7 @@ module Sequel
629
772
  # DB[:items].select(:a, :b) # SELECT a, b FROM items
630
773
  # DB[:items].select{[a, sum(b)]} # SELECT a, sum(b) FROM items
631
774
  def select(*columns, &block)
632
- columns += Array(Sequel.virtual_row(&block)) if block
775
+ virtual_row_columns(columns, block)
633
776
  m = []
634
777
  columns.each do |i|
635
778
  i.is_a?(Hash) ? m.concat(i.map{|k, v| SQL::AliasedExpression.new(k,v)}) : m << i
@@ -637,11 +780,19 @@ module Sequel
637
780
  clone(:select => m)
638
781
  end
639
782
 
640
- # Returns a copy of the dataset selecting the wildcard.
783
+ # Returns a copy of the dataset selecting the wildcard if no arguments
784
+ # are given. If arguments are given, treat them as tables and select
785
+ # all columns (using the wildcard) from each table.
641
786
  #
642
787
  # DB[:items].select(:a).select_all # SELECT * FROM items
643
- def select_all
644
- clone(:select => nil)
788
+ # DB[:items].select_all(:items) # SELECT items.* FROM items
789
+ # DB[:items].select_all(:items, :foo) # SELECT items.*, foo.* FROM items
790
+ def select_all(*tables)
791
+ if tables.empty?
792
+ clone(:select => nil)
793
+ else
794
+ select(*tables.map{|t| i, a = split_alias(t); a || i}.map{|t| SQL::ColumnAll.new(t)})
795
+ end
645
796
  end
646
797
 
647
798
  # Returns a copy of the dataset with the given columns added
@@ -653,10 +804,29 @@ module Sequel
653
804
  # DB[:items].select_append(:b) # SELECT *, b FROM items
654
805
  def select_append(*columns, &block)
655
806
  cur_sel = @opts[:select]
656
- cur_sel = [WILDCARD] if !cur_sel || cur_sel.empty?
807
+ if !cur_sel || cur_sel.empty?
808
+ unless supports_select_all_and_column?
809
+ return select_all(*(Array(@opts[:from]) + Array(@opts[:join]))).select_more(*columns, &block)
810
+ end
811
+ cur_sel = [WILDCARD]
812
+ end
657
813
  select(*(cur_sel + columns), &block)
658
814
  end
659
815
 
816
+ # Set both the select and group clauses with the given +columns+.
817
+ # Column aliases may be supplied, and will be included in the select clause.
818
+ # This also takes a virtual row block similar to +filter+.
819
+ #
820
+ # DB[:items].select_group(:a, :b)
821
+ # # SELECT a, b FROM items GROUP BY a, b
822
+ #
823
+ # DB[:items].select_group(:c___a){f(c2)}
824
+ # # SELECT c AS a, f(c2) FROM items GROUP BY c, f(c2)
825
+ def select_group(*columns, &block)
826
+ virtual_row_columns(columns, block)
827
+ select(*columns).group(*columns.map{|c| unaliased_identifier(c)})
828
+ end
829
+
660
830
  # Returns a copy of the dataset with the given columns added
661
831
  # to the existing selected columns. If no columns are currently selected
662
832
  # it will just select the columns given.
@@ -670,7 +840,7 @@ module Sequel
670
840
  end
671
841
 
672
842
  # Set the server for this dataset to use. Used to pick a specific database
673
- # shard to run a query against, or to override the default (which is SELECT uses
843
+ # shard to run a query against, or to override the default (where SELECT uses
674
844
  # :read_only database and all other queries use the :default database). This
675
845
  # method is always available but is only useful when database sharding is being
676
846
  # used.
@@ -702,6 +872,22 @@ module Sequel
702
872
  clone(:overrides=>hash.merge(@opts[:overrides]||{}))
703
873
  end
704
874
 
875
+ # Unbind bound variables from this dataset's filter and return an array of two
876
+ # objects. The first object is a modified dataset where the filter has been
877
+ # replaced with one that uses bound variable placeholders. The second object
878
+ # is the hash of unbound variables. You can then prepare and execute (or just
879
+ # call) the dataset with the bound variables to get results.
880
+ #
881
+ # ds, bv = DB[:items].filter(:a=>1).unbind
882
+ # ds # SELECT * FROM items WHERE (a = $a)
883
+ # bv # {:a => 1}
884
+ # ds.call(:select, bv)
885
+ def unbind
886
+ u = Unbinder.new
887
+ ds = clone(:where=>u.transform(opts[:where]), :join=>u.transform(opts[:join]))
888
+ [ds, u.binds]
889
+ end
890
+
705
891
  # Returns a copy of the dataset with no filters (HAVING or WHERE clause) applied.
706
892
  #
707
893
  # DB[:items].group(:a).having(:a=>1).where(:b).unfiltered
@@ -726,8 +912,8 @@ module Sequel
726
912
  # :all :: Set to true to use UNION ALL instead of UNION, so duplicate rows can occur
727
913
  # :from_self :: Set to false to not wrap the returned dataset in a from_self, use with care.
728
914
  #
729
- # DB[:items].union(DB[:other_items]).sql
730
- # #=> "SELECT * FROM items UNION SELECT * FROM other_items"
915
+ # DB[:items].union(DB[:other_items])
916
+ # # SELECT * FROM (SELECT * FROM items UNION SELECT * FROM other_items) AS t1
731
917
  #
732
918
  # DB[:items].union(DB[:other_items], :all=>true, :from_self=>false)
733
919
  # # SELECT * FROM items UNION ALL SELECT * FROM other_items
@@ -774,7 +960,12 @@ module Sequel
774
960
  # # WITH items AS (SELECT * FROM syx WHERE (name LIKE 'A%')) SELECT * FROM items
775
961
  def with(name, dataset, opts={})
776
962
  raise(Error, 'This datatset does not support common table expressions') unless supports_cte?
777
- clone(:with=>(@opts[:with]||[]) + [opts.merge(:name=>name, :dataset=>dataset)])
963
+ if hoist_cte?(dataset)
964
+ s, ds = hoist_cte(dataset)
965
+ s.with(name, ds, opts)
966
+ else
967
+ clone(:with=>(@opts[:with]||[]) + [opts.merge(:name=>name, :dataset=>dataset)])
968
+ end
778
969
  end
779
970
 
780
971
  # Add a recursive common table expression (CTE) with the given name, a dataset that
@@ -796,20 +987,50 @@ module Sequel
796
987
  # # SELECT i AS id, pi AS parent_id FROM t
797
988
  def with_recursive(name, nonrecursive, recursive, opts={})
798
989
  raise(Error, 'This datatset does not support common table expressions') unless supports_cte?
799
- clone(:with=>(@opts[:with]||[]) + [opts.merge(:recursive=>true, :name=>name, :dataset=>nonrecursive.union(recursive, {:all=>opts[:union_all] != false, :from_self=>false}))])
990
+ if hoist_cte?(nonrecursive)
991
+ s, ds = hoist_cte(nonrecursive)
992
+ s.with_recursive(name, ds, recursive, opts)
993
+ elsif hoist_cte?(recursive)
994
+ s, ds = hoist_cte(recursive)
995
+ s.with_recursive(name, nonrecursive, ds, opts)
996
+ else
997
+ clone(:with=>(@opts[:with]||[]) + [opts.merge(:recursive=>true, :name=>name, :dataset=>nonrecursive.union(recursive, {:all=>opts[:union_all] != false, :from_self=>false}))])
998
+ end
800
999
  end
801
1000
 
802
1001
  # Returns a copy of the dataset with the static SQL used. This is useful if you want
803
1002
  # to keep the same row_proc/graph, but change the SQL used to custom SQL.
804
1003
  #
805
1004
  # DB[:items].with_sql('SELECT * FROM foo') # SELECT * FROM foo
1005
+ #
1006
+ # You can use placeholders in your SQL and provide arguments for those placeholders:
1007
+ #
1008
+ # DB[:items].with_sql('SELECT ? FROM foo', 1) # SELECT 1 FROM foo
1009
+ #
1010
+ # You can also provide a method name and arguments to call to get the SQL:
1011
+ #
1012
+ # DB[:items].with_sql(:insert_sql, :b=>1) # INSERT INTO items (b) VALUES (1)
806
1013
  def with_sql(sql, *args)
807
- sql = SQL::PlaceholderLiteralString.new(sql, args) unless args.empty?
1014
+ if sql.is_a?(Symbol)
1015
+ sql = send(sql, *args)
1016
+ else
1017
+ sql = SQL::PlaceholderLiteralString.new(sql, args) unless args.empty?
1018
+ end
808
1019
  clone(:sql=>sql)
809
1020
  end
810
1021
 
811
1022
  protected
812
1023
 
1024
+ # Add the dataset to the list of compounds
1025
+ def compound_clone(type, dataset, opts)
1026
+ if hoist_cte?(dataset)
1027
+ s, ds = hoist_cte(dataset)
1028
+ return s.compound_clone(type, ds, opts)
1029
+ end
1030
+ ds = compound_from_self.clone(:compounds=>Array(@opts[:compounds]).map{|x| x.dup} + [[type, dataset.compound_from_self, opts[:all]]])
1031
+ opts[:from_self] == false ? ds : ds.from_self(opts)
1032
+ end
1033
+
813
1034
  # Return true if the dataset has a non-nil value for any key in opts.
814
1035
  def options_overlap(opts)
815
1036
  !(@opts.collect{|k,v| k unless v.nil?}.compact & opts).empty?
@@ -823,22 +1044,22 @@ module Sequel
823
1044
 
824
1045
  private
825
1046
 
826
- # Internal filter method so it works on either the having or where clauses.
827
- def _filter(clause, *cond, &block)
1047
+ # Internal filter/exclude method so it works on either the having or where clauses.
1048
+ def _filter_or_exclude(invert, clause, *cond, &block)
828
1049
  cond = cond.first if cond.size == 1
829
1050
  if cond.respond_to?(:empty?) && cond.empty? && !block
830
1051
  clone
831
1052
  else
832
1053
  cond = filter_expr(cond, &block)
1054
+ cond = SQL::BooleanExpression.invert(cond) if invert
833
1055
  cond = SQL::BooleanExpression.new(:AND, @opts[clause], cond) if @opts[clause]
834
1056
  clone(clause => cond)
835
1057
  end
836
1058
  end
837
-
838
- # Add the dataset to the list of compounds
839
- def compound_clone(type, dataset, opts)
840
- ds = compound_from_self.clone(:compounds=>Array(@opts[:compounds]).map{|x| x.dup} + [[type, dataset.compound_from_self, opts[:all]]])
841
- opts[:from_self] == false ? ds : ds.from_self(opts)
1059
+
1060
+ # Internal filter method so it works on either the having or where clauses.
1061
+ def _filter(clause, *cond, &block)
1062
+ _filter_or_exclude(false, clause, *cond, &block)
842
1063
  end
843
1064
 
844
1065
  # SQL expression object based on the expr type. See +filter+.
@@ -867,7 +1088,13 @@ module Sequel
867
1088
  when Symbol, SQL::Expression
868
1089
  expr
869
1090
  when TrueClass, FalseClass
870
- SQL::BooleanExpression.new(:NOOP, expr)
1091
+ if supports_where_true?
1092
+ SQL::BooleanExpression.new(:NOOP, expr)
1093
+ elsif expr
1094
+ SQL::Constants::SQLTRUE
1095
+ else
1096
+ SQL::Constants::SQLFALSE
1097
+ end
871
1098
  when String
872
1099
  LiteralString.new("(#{expr})")
873
1100
  else
@@ -875,11 +1102,23 @@ module Sequel
875
1102
  end
876
1103
  end
877
1104
 
1105
+ # Return two datasets, the first a clone of the receiver with the WITH
1106
+ # clause from the given dataset added to it, and the second a clone of
1107
+ # the given dataset with the WITH clause removed.
1108
+ def hoist_cte(ds)
1109
+ [clone(:with => (opts[:with] || []) + ds.opts[:with]), ds.clone(:with => nil)]
1110
+ end
1111
+
1112
+ # Whether CTEs need to be hoisted from the given ds into the current ds.
1113
+ def hoist_cte?(ds)
1114
+ ds.is_a?(Dataset) && ds.opts[:with] && !supports_cte_in_subqueries?
1115
+ end
1116
+
878
1117
  # Inverts the given order by breaking it into a list of column references
879
1118
  # and inverting them.
880
1119
  #
881
- # DB[:items].invert_order([:id.desc]]) #=> [:id]
882
- # DB[:items].invert_order(:category, :price.desc]) #=> [:category.desc, :price]
1120
+ # DB[:items].invert_order([Sequel.desc(:id)]]) #=> [Sequel.asc(:id)]
1121
+ # DB[:items].invert_order([:category, Sequel.desc(:price)]) #=> [Sequel.desc(:category), Sequel.asc(:price)]
883
1122
  def invert_order(order)
884
1123
  return nil unless order
885
1124
  new_order = []
@@ -892,5 +1131,17 @@ module Sequel
892
1131
  end
893
1132
  end
894
1133
  end
1134
+
1135
+ # Return self if the dataset already has a server, or a cloned dataset with the
1136
+ # default server otherwise.
1137
+ def default_server
1138
+ @opts[:server] ? self : clone(:server=>:default)
1139
+ end
1140
+
1141
+ # Treat the +block+ as a virtual_row block if not +nil+ and
1142
+ # add the resulting columns to the +columns+ array (modifies +columns+).
1143
+ def virtual_row_columns(columns, block)
1144
+ columns.concat(Array(Sequel.virtual_row(&block))) if block
1145
+ end
895
1146
  end
896
1147
  end