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
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
67
84
 
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__)
85
+ private
86
+
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,332 @@ 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
+ # Order the given argument descending.
381
+ # Options:
382
+ #
383
+ # :nulls :: Set to :first to use NULLS FIRST (so NULL values are ordered
384
+ # before other values), or :last to use NULLS LAST (so NULL values
385
+ # are ordered after other values).
386
+ #
387
+ # Sequel.desc(:a) # b DESC
388
+ # Sequel.desc(:b, :nulls=>:first) # b DESC NULLS FIRST
389
+ def desc(arg, opts={})
390
+ SQL::OrderedExpression.new(arg, true, opts)
391
+ end
392
+
393
+ # Wraps the given object in an appropriate Sequel wrapper.
394
+ # If the given object is already a Sequel object, return it directly.
395
+ # For condition specifiers (hashes and arrays of two pairs), true, and false,
396
+ # return a boolean expressions. For numeric objects, return a numeric
397
+ # expression. For strings, return a string expression. For procs or when
398
+ # the method is passed a block, evaluate it as a virtual row and wrap it
399
+ # appropriately. In all other cases, use a generic wrapper.
400
+ #
401
+ # This method allows you to construct SQL expressions that are difficult
402
+ # to construct via other methods. For example:
403
+ #
404
+ # Sequel.expr(1) - :a # SQL: (1 - a)
405
+ def expr(arg=(no_arg=true), &block)
406
+ if block_given?
407
+ if no_arg
408
+ return expr(block)
409
+ else
410
+ raise Error, 'cannot provide both an argument and a block to Sequel.expr'
411
+ end
412
+ elsif no_arg
413
+ raise Error, 'must provide either an argument or a block to Sequel.expr'
414
+ end
415
+
416
+ case arg
417
+ when Symbol
418
+ t, c, a = Sequel.split_symbol(arg)
419
+
420
+ arg = if t
421
+ SQL::QualifiedIdentifier.new(t, c)
422
+ else
423
+ SQL::Identifier.new(c)
424
+ end
425
+
426
+ if a
427
+ arg = SQL::AliasedExpression.new(arg, a)
428
+ end
429
+
430
+ arg
431
+ when SQL::Expression, LiteralString, SQL::Blob
432
+ arg
433
+ when Hash
434
+ SQL::BooleanExpression.from_value_pairs(arg, :AND)
435
+ when Array
436
+ if condition_specifier?(arg)
437
+ SQL::BooleanExpression.from_value_pairs(arg, :AND)
438
+ else
439
+ SQL::Wrapper.new(arg)
440
+ end
441
+ when Numeric
442
+ SQL::NumericExpression.new(:NOOP, arg)
443
+ when String
444
+ SQL::StringExpression.new(:NOOP, arg)
445
+ when TrueClass, FalseClass
446
+ SQL::BooleanExpression.new(:NOOP, arg)
447
+ when Proc
448
+ expr(virtual_row(&arg))
449
+ else
450
+ SQL::Wrapper.new(arg)
451
+ end
452
+ end
453
+
454
+ # Extract a datetime_part (e.g. year, month) from the given
455
+ # expression:
456
+ #
457
+ # Sequel.extract(:year, :date) # extract(year FROM "date")
458
+ def extract(datetime_part, exp)
459
+ SQL::NumericExpression.new(:extract, datetime_part, exp)
460
+ end
461
+
462
+ # Returns a <tt>Sequel::SQL::Function</tt> with the function name
463
+ # and the given arguments.
464
+ #
465
+ # Sequel.function(:now) # SQL: now()
466
+ # Sequel.function(:substr, :a, 1) # SQL: substr(a, 1)
467
+ def function(name, *args)
468
+ SQL::Function.new(name, *args)
469
+ end
470
+
471
+ # Return the argument wrapped as an <tt>SQL::Identifier</tt>.
472
+ #
473
+ # Sequel.identifier(:a__b) # "a__b"
474
+ def identifier(name)
475
+ SQL::Identifier.new(name)
476
+ end
477
+
478
+ # Return a <tt>Sequel::SQL::StringExpression</tt> representing an SQL string made up of the
479
+ # concatenation of the given array's elements. If an argument is passed,
480
+ # it is used in between each element of the array in the SQL
481
+ # concatenation.
482
+ #
483
+ # Sequel.join([:a]) # SQL: a
484
+ # Sequel.join([:a, :b]) # SQL: a || b
485
+ # Sequel.join([:a, 'b']) # SQL: a || 'b'
486
+ # Sequel.join(['a', :b], ' ') # SQL: 'a' || ' ' || b
487
+ def join(args, joiner=nil)
488
+ raise Error, 'argument to Sequel.join must be an array' unless args.is_a?(Array)
489
+ if joiner
490
+ args = args.zip([joiner]*args.length).flatten
491
+ args.pop
492
+ end
493
+
494
+ return SQL::StringExpression.new(:NOOP, '') if args.empty?
495
+
496
+ args = args.map do |a|
497
+ case a
498
+ when Symbol, ::Sequel::SQL::Expression, ::Sequel::LiteralString, TrueClass, FalseClass, NilClass
499
+ a
500
+ else
501
+ a.to_s
502
+ end
503
+ end
504
+ SQL::StringExpression.new(:'||', *args)
505
+ end
506
+
507
+ # Create a <tt>BooleanExpression</tt> case insensitive (if the database supports it) pattern match of the receiver with
508
+ # the given patterns. See <tt>SQL::StringExpression.like</tt>.
509
+ #
510
+ # Sequel.ilike(:a, 'A%') # "a" ILIKE 'A%'
511
+ def ilike(*args)
512
+ SQL::StringExpression.like(*(args << {:case_insensitive=>true}))
513
+ end
514
+
515
+ # Create a <tt>SQL::BooleanExpression</tt> case sensitive (if the database supports it) pattern match of the receiver with
516
+ # the given patterns. See <tt>SQL::StringExpression.like</tt>.
517
+ #
518
+ # Sequel.like(:a, 'A%') # "a" LIKE 'A%'
519
+ def like(*args)
520
+ SQL::StringExpression.like(*args)
521
+ end
522
+
523
+ # Converts a string into a <tt>Sequel::LiteralString</tt>, in order to override string
524
+ # literalization, e.g.:
525
+ #
526
+ # DB[:items].filter(:abc => 'def').sql #=>
527
+ # "SELECT * FROM items WHERE (abc = 'def')"
528
+ #
529
+ # DB[:items].filter(:abc => Sequel.lit('def')).sql #=>
530
+ # "SELECT * FROM items WHERE (abc = def)"
531
+ #
532
+ # You can also provide arguments, to create a <tt>Sequel::SQL::PlaceholderLiteralString</tt>:
533
+ #
534
+ # DB[:items].select{|o| o.count(Sequel.lit('DISTINCT ?', :a))}.sql #=>
535
+ # "SELECT count(DISTINCT a) FROM items"
536
+ def lit(s, *args) # core_sql ignore
537
+ if args.empty?
538
+ if s.is_a?(LiteralString)
539
+ s
540
+ else
541
+ LiteralString.new(s)
542
+ end
543
+ else
544
+ SQL::PlaceholderLiteralString.new(s, args)
545
+ end
546
+ end
547
+
548
+ # Return a <tt>Sequel::SQL::BooleanExpression</tt> created from the condition
549
+ # specifier, matching none of the conditions.
550
+ #
551
+ # Sequel.negate(:a=>true) # SQL: a IS NOT TRUE
552
+ # Sequel.negate([[:a, true]]) # SQL: a IS NOT TRUE
553
+ # Sequel.negate([[:a, 1], [:b, 2]]) # SQL: ((a != 1) AND (b != 2))
554
+ def negate(arg)
555
+ if condition_specifier?(arg)
556
+ SQL::BooleanExpression.from_value_pairs(arg, :AND, true)
557
+ else
558
+ raise Error, 'must pass a conditions specifier to Sequel.negate'
559
+ end
560
+ end
561
+
562
+ # Return a <tt>Sequel::SQL::BooleanExpression</tt> created from the condition
563
+ # specifier, matching any of the conditions.
564
+ #
565
+ # Sequel.or(:a=>true) # SQL: a IS TRUE
566
+ # Sequel.or([[:a, true]]) # SQL: a IS TRUE
567
+ # Sequel.or([[:a, 1], [:b, 2]]) # SQL: ((a = 1) OR (b = 2))
568
+ def or(arg)
569
+ if condition_specifier?(arg)
570
+ SQL::BooleanExpression.from_value_pairs(arg, :OR, false)
571
+ else
572
+ raise Error, 'must pass a conditions specifier to Sequel.or'
573
+ end
574
+ end
575
+
576
+ # Create a qualified identifier with the given qualifier and identifier
577
+ #
578
+ # Sequel.qualify(:table, :column) # "table"."column"
579
+ # Sequel.qualify(:schema, :table) # "schema"."table"
580
+ # Sequel.qualify(:table, :column).qualify(:schema) # "schema"."table"."column"
581
+ def qualify(qualifier, identifier)
582
+ SQL::QualifiedIdentifier.new(qualifier, identifier)
583
+ end
584
+
585
+ # Return an <tt>SQL::Subscript</tt> with the given arguments, representing an
586
+ # SQL array access.
587
+ #
588
+ # Sequel.subscript(:array, 1) # array[1]
589
+ # Sequel.subscript(:array, 1, 2) # array[1, 2]
590
+ # Sequel.subscript(:array, [1, 2]) # array[1, 2]
591
+ def subscript(exp, *subs)
592
+ SQL::Subscript.new(exp, subs.flatten)
593
+ end
594
+
595
+ # Return an emulated function call for trimming a string of spaces from
596
+ # both sides (similar to ruby's String#strip).
597
+ #
598
+ # Sequel.trim(:a) # trim(a) -- Most databases
599
+ # Sequel.trim(:a) # ltrim(rtrim(a)) -- Microsoft SQL Server
600
+ def trim(arg)
601
+ SQL::EmulatedFunction.new(:trim, arg)
602
+ end
603
+
604
+ # Return a <tt>SQL::ValueList</tt> created from the given array. Used if the array contains
605
+ # all two element arrays and you want it treated as an SQL value list (IN predicate)
606
+ # instead of as a conditions specifier (similar to a hash). This is not necessary if you are using
607
+ # this array as a value in a filter, but may be necessary if you are using it as a
608
+ # value with placeholder SQL:
609
+ #
610
+ # DB[:a].filter([:a, :b]=>[[1, 2], [3, 4]]) # SQL: (a, b) IN ((1, 2), (3, 4))
611
+ # DB[:a].filter('(a, b) IN ?', [[1, 2], [3, 4]]) # SQL: (a, b) IN ((1 = 2) AND (3 = 4))
612
+ # DB[:a].filter('(a, b) IN ?', Sequel.value_list([[1, 2], [3, 4]])) # SQL: (a, b) IN ((1, 2), (3, 4))
613
+ def value_list(arg)
614
+ raise Error, 'argument to Sequel.value_list must be an array' unless arg.is_a?(Array)
615
+ SQL::ValueList.new(arg)
616
+ end
617
+ end
618
+
267
619
  # Holds methods that are used to cast objects to different SQL types.
268
620
  module CastMethods
269
621
  # Cast the reciever to the given SQL type. You can specify a ruby class as a type,
270
622
  # and it is handled similarly to using a database independent type in the schema methods.
271
623
  #
272
- # :a.cast(:integer) # CAST(a AS integer)
273
- # :a.cast(String) # CAST(a AS varchar(255))
624
+ # Sequel.function(:func).cast(:integer) # CAST(func() AS integer)
625
+ # Sequel.function(:func).cast(String) # CAST(func() AS varchar(255))
274
626
  def cast(sql_type)
275
627
  Cast.new(self, sql_type)
276
628
  end
@@ -279,8 +631,8 @@ module Sequel
279
631
  # and return the result as a +NumericExpression+, so you can use the bitwise operators
280
632
  # on the result.
281
633
  #
282
- # :a.cast_numeric # CAST(a AS integer)
283
- # :a.cast_numeric(Float) # CAST(a AS double precision)
634
+ # Sequel.function(:func).cast_numeric # CAST(func() AS integer)
635
+ # Sequel.function(:func).cast_numeric(Float) # CAST(func() AS double precision)
284
636
  def cast_numeric(sql_type = nil)
285
637
  cast(sql_type || Integer).sql_number
286
638
  end
@@ -289,13 +641,13 @@ module Sequel
289
641
  # and return the result as a +StringExpression+, so you can use +
290
642
  # directly on the result for SQL string concatenation.
291
643
  #
292
- # :a.cast_string # CAST(a AS varchar(255))
293
- # :a.cast_string(:text) # CAST(a AS text)
644
+ # Sequel.function(:func).cast_string # CAST(func() AS varchar(255))
645
+ # Sequel.function(:func).cast_string(:text) # CAST(func() AS text)
294
646
  def cast_string(sql_type = nil)
295
647
  cast(sql_type || String).sql_string
296
648
  end
297
649
  end
298
-
650
+
299
651
  # Adds methods that allow you to treat an object as an instance of a specific
300
652
  # +ComplexExpression+ subclass. This is useful if another library
301
653
  # overrides the methods defined by Sequel.
@@ -310,18 +662,14 @@ module Sequel
310
662
  #
311
663
  # :price.sql_number/10 > 100
312
664
  module ComplexExpressionMethods
313
- # Extract a datetime_part (e.g. year, month) from self:
665
+ # Extract a datetime part (e.g. year, month) from self:
314
666
  #
315
667
  # :date.extract(:year) # extract(year FROM "date")
316
668
  #
317
669
  # Also has the benefit of returning the result as a
318
670
  # 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
671
  def extract(datetime_part)
324
- Function.new(:extract, PlaceholderLiteralString.new("#{datetime_part} FROM ?", [self])).sql_number
672
+ NumericExpression.new(:extract, datetime_part, self)
325
673
  end
326
674
 
327
675
  # Return a BooleanExpression representation of +self+.
@@ -346,55 +694,21 @@ module Sequel
346
694
  end
347
695
  end
348
696
 
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
697
  # 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>).
698
+ # used in a numeric or string context in SQL.
363
699
  #
364
- # 'a'.lit > :b # a > "b"
365
- # 'a'.lit < :b # a > "b"
366
- # 'a'.lit >= :b # a >= "b"
367
- # 'a'.lit <= :b # a <= "b"
700
+ # Sequel.lit('a') > :b # a > "b"
701
+ # Sequel.lit('a') < :b # a > "b"
702
+ # Sequel.lit('a') >= :b # a >= "b"
703
+ # Sequel.lit('a') <= :b # a <= "b"
368
704
  module InequalityMethods
369
705
  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
706
+ module_eval("def #{o}(o) BooleanExpression.new(#{o.inspect}, self, o) end", __FILE__, __LINE__)
378
707
  end
379
708
  end
380
709
 
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.
710
+ # Only exists for backwards compatibility, ignore it.
386
711
  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
712
  end
399
713
 
400
714
  # This module includes the standard mathematical methods (+, -, *, and /)
@@ -405,15 +719,66 @@ module Sequel
405
719
  # :a - :b # "a" - "b"
406
720
  # :a * :b # "a" * "b"
407
721
  # :a / :b # "a" / "b"
722
+ #
723
+ # One exception to this is if + is called with a +String+ or +StringExpression+,
724
+ # in which case the || operator is used instead of the + operator:
725
+ #
726
+ # :a + 'b' # "a" || 'b'
408
727
  module NumericMethods
409
728
  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)
729
+ module_eval("def #{o}(o) NumericExpression.new(#{o.inspect}, self, o) end", __FILE__, __LINE__) unless o == :+
730
+ end
731
+
732
+ # Use || as the operator when called with StringExpression and String instances,
733
+ # and the + operator for LiteralStrings and all other types.
734
+ def +(ce)
735
+ case ce
736
+ when LiteralString
737
+ NumericExpression.new(:+, self, ce)
738
+ when StringExpression, String
739
+ StringExpression.new(:'||', self, ce)
740
+ else
741
+ NumericExpression.new(:+, self, ce)
742
+ end
743
+ end
744
+ end
745
+
746
+ # These methods are designed as replacements for the core extension operator
747
+ # methods, so that Sequel is still easy to use if the core extensions are not
748
+ # enabled.
749
+ #
750
+ # The following methods are defined via metaprogramming: +, -, *, /, &, |.
751
+ # The +, -, *, and / operators return numeric expressions combining all the
752
+ # arguments with the appropriate operator, and the & and | operators return
753
+ # boolean expressions combining all of the arguments with either AND or OR.
754
+ module OperatorBuilders
755
+ %w'+ - * /'.each do |op|
756
+ class_eval(<<-END, __FILE__, __LINE__ + 1)
757
+ def #{op}(*args)
758
+ SQL::NumericExpression.new(:#{op}, *args)
416
759
  end
760
+ END
761
+ end
762
+
763
+ {'&'=>'AND', '|'=>'OR'}.each do |m, op|
764
+ class_eval(<<-END, __FILE__, __LINE__ + 1)
765
+ def #{m}(*args)
766
+ SQL::BooleanExpression.new(:#{op}, *args)
767
+ end
768
+ END
769
+ end
770
+
771
+ # Invert the given expression. Returns a <tt>Sequel::SQL::BooleanExpression</tt>
772
+ # created from this argument, not matching all of the conditions.
773
+ #
774
+ # Sequel.~(nil) # SQL: NOT NULL
775
+ # Sequel.~([[:a, true]]) # SQL: a IS NOT TRUE
776
+ # Sequel.~([[:a, 1], [:b, [2, 3]]]) # SQL: a != 1 OR b NOT IN (2, 3)
777
+ def ~(arg)
778
+ if condition_specifier?(arg)
779
+ SQL::BooleanExpression.from_value_pairs(arg, :OR, true)
780
+ else
781
+ SQL::BooleanExpression.invert(arg)
417
782
  end
418
783
  end
419
784
  end
@@ -443,13 +808,25 @@ module Sequel
443
808
  end
444
809
 
445
810
  # 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.
811
+ # names with a table or table names with a schema, and the * method for returning all columns in
812
+ # the identifier if no arguments are given.
447
813
  module QualifyingMethods
814
+ # If no arguments are given, return an SQL::ColumnAll:
815
+ #
816
+ # Sequel.expr(:a__b).* # a.b.*
817
+ def *(ce=(arg=false;nil))
818
+ if arg == false
819
+ Sequel::SQL::ColumnAll.new(self)
820
+ else
821
+ super(ce)
822
+ end
823
+ end
824
+
448
825
  # Qualify the receiver with the given +qualifier+ (table for column/schema for table).
449
826
  #
450
- # :column.qualify(:table) # "table"."column"
451
- # :table.qualify(:schema) # "schema"."table"
452
- # :column.qualify(:table).qualify(:schema) # "schema"."table"."column"
827
+ # Sequel.expr(:column).qualify(:table) # "table"."column"
828
+ # Sequel.expr(:table).qualify(:schema) # "schema"."table"
829
+ # Sequel.qualify(:table, :column).qualify(:schema) # "schema"."table"."column"
453
830
  def qualify(qualifier)
454
831
  QualifiedIdentifier.new(qualifier, self)
455
832
  end
@@ -523,8 +900,16 @@ module Sequel
523
900
  # stored as a blob type in the database. Sequel represents binary data as a Blob object because
524
901
  # most database engines require binary data to be escaped differently than regular strings.
525
902
  class Blob < ::String
526
- # Returns +self+, used so that Blobs don't get wrapped in multiple
527
- # levels.
903
+ include SQL::AliasMethods
904
+ include SQL::CastMethods
905
+
906
+ # Return a LiteralString with the same content if no args are given, otherwise
907
+ # return a SQL::PlaceholderLiteralString with the current string and the given args.
908
+ def lit(*args)
909
+ args.empty? ? LiteralString.new(self) : SQL::PlaceholderLiteralString.new(self, args)
910
+ end
911
+
912
+ # Returns +self+, since it is already a blob.
528
913
  def to_sequel_blob
529
914
  self
530
915
  end
@@ -595,10 +980,27 @@ module Sequel
595
980
  end
596
981
  when StringExpression, NumericExpression
597
982
  raise(Sequel::Error, "cannot invert #{ce.inspect}")
983
+ when Constant
984
+ CONSTANT_INVERSIONS[ce] || raise(Sequel::Error, "cannot invert #{ce.inspect}")
598
985
  else
599
986
  BooleanExpression.new(:NOT, ce)
600
987
  end
601
988
  end
989
+
990
+ # Always use an AND operator for & on BooleanExpressions
991
+ def &(ce)
992
+ BooleanExpression.new(:AND, self, ce)
993
+ end
994
+
995
+ # Always use an OR operator for | on BooleanExpressions
996
+ def |(ce)
997
+ BooleanExpression.new(:OR, self, ce)
998
+ end
999
+
1000
+ # Return self instead of creating a new object to save on memory.
1001
+ def sql_boolean
1002
+ self
1003
+ end
602
1004
  end
603
1005
 
604
1006
  # Represents an SQL CASE expression, used for conditional branching in SQL.
@@ -666,6 +1068,21 @@ module Sequel
666
1068
  include CastMethods
667
1069
  include OrderMethods
668
1070
  include SubscriptMethods
1071
+
1072
+ # Return a BooleanExpression with the same op and args.
1073
+ def sql_boolean
1074
+ BooleanExpression.new(self.op, *self.args)
1075
+ end
1076
+
1077
+ # Return a NumericExpression with the same op and args.
1078
+ def sql_number
1079
+ NumericExpression.new(self.op, *self.args)
1080
+ end
1081
+
1082
+ # Return a StringExpression with the same op and args.
1083
+ def sql_string
1084
+ StringExpression.new(self.op, *self.args)
1085
+ end
669
1086
  end
670
1087
 
671
1088
  # Represents constants or psuedo-constants (e.g. +CURRENT_DATE+) in SQL.
@@ -688,7 +1105,7 @@ module Sequel
688
1105
 
689
1106
  # Represents inverse boolean constants (currently only +NOTNULL+). A
690
1107
  # special class to allow for special behavior.
691
- class NegativeBooleanConstant < BooleanConstant
1108
+ class NegativeBooleanConstant < Constant
692
1109
  to_s_method :negative_boolean_constant_sql, '@constant'
693
1110
  end
694
1111
 
@@ -706,14 +1123,20 @@ module Sequel
706
1123
  NOTNULL = NegativeBooleanConstant.new(nil)
707
1124
  end
708
1125
 
1126
+ class ComplexExpression
1127
+ # A hash of the opposite for each constant, used for inverting constants.
1128
+ CONSTANT_INVERSIONS = {Constants::TRUE=>Constants::FALSE, Constants::FALSE=>Constants::TRUE,
1129
+ Constants::NULL=>Constants::NOTNULL, Constants::NOTNULL=>Constants::NULL}
1130
+ end
1131
+
709
1132
  # Represents an SQL function call.
710
1133
  class Function < GenericExpression
711
- # The array of arguments to pass to the function (may be blank)
712
- attr_reader :args
713
-
714
1134
  # The SQL function to call
715
1135
  attr_reader :f
716
1136
 
1137
+ # The array of arguments to pass to the function (may be blank)
1138
+ attr_reader :args
1139
+
717
1140
  # Set the functions and args to the given arguments
718
1141
  def initialize(f, *args)
719
1142
  @f, @args = f, args
@@ -721,6 +1144,12 @@ module Sequel
721
1144
 
722
1145
  to_s_method :function_sql
723
1146
  end
1147
+
1148
+ # Represents an SQL function call that is translated/emulated
1149
+ # on databases that lack support for such a function.
1150
+ class EmulatedFunction < Function
1151
+ to_s_method :emulated_function_sql
1152
+ end
724
1153
 
725
1154
  class GenericExpression
726
1155
  include AliasMethods
@@ -806,15 +1235,16 @@ module Sequel
806
1235
  # required for the prepared statement support and for database-specific
807
1236
  # literalization.
808
1237
  class PlaceholderLiteralString < GenericExpression
1238
+ # The literal string containing placeholders. This can also be an array
1239
+ # of strings, where each arg in args goes between the string elements.
1240
+ attr_reader :str
1241
+
809
1242
  # The arguments that will be subsituted into the placeholders.
810
1243
  # Either an array of unnamed placeholders (which will be substituted in
811
1244
  # order for ? characters), or a hash of named placeholders (which will be
812
1245
  # substituted for :key phrases).
813
1246
  attr_reader :args
814
1247
 
815
- # The literal string containing placeholders
816
- attr_reader :str
817
-
818
1248
  # Whether to surround the expression with parantheses
819
1249
  attr_reader :parens
820
1250
 
@@ -834,7 +1264,16 @@ module Sequel
834
1264
  include BitwiseMethods
835
1265
  include NumericMethods
836
1266
  include InequalityMethods
837
- include NoBooleanInputMethods
1267
+
1268
+ # Always use + for + operator for NumericExpressions.
1269
+ def +(ce)
1270
+ NumericExpression.new(:+, self, ce)
1271
+ end
1272
+
1273
+ # Return self instead of creating a new object to save on memory.
1274
+ def sql_number
1275
+ self
1276
+ end
838
1277
  end
839
1278
 
840
1279
  # Represents a column/expression to order the result set by.
@@ -880,18 +1319,18 @@ module Sequel
880
1319
  class QualifiedIdentifier < GenericExpression
881
1320
  include QualifyingMethods
882
1321
 
883
- # The column/table referenced
884
- attr_reader :column
885
-
886
1322
  # The table/schema qualifying the reference
887
1323
  attr_reader :table
888
1324
 
1325
+ # The column/table referenced
1326
+ attr_reader :column
1327
+
889
1328
  # Set the table and column to the given arguments
890
1329
  def initialize(table, column)
891
1330
  @table, @column = table, column
892
1331
  end
893
1332
 
894
- to_s_method :qualified_identifier_sql
1333
+ to_s_method :qualified_identifier_sql, "@table, @column"
895
1334
  end
896
1335
 
897
1336
  # Subclass of +ComplexExpression+ where the expression results
@@ -900,7 +1339,6 @@ module Sequel
900
1339
  include StringMethods
901
1340
  include StringConcatenationMethods
902
1341
  include InequalityMethods
903
- include NoBooleanInputMethods
904
1342
 
905
1343
  # Map of [regexp, case_insenstive] to +ComplexExpression+ operator symbol
906
1344
  LIKE_MAP = {[true, true]=>:'~*', [true, false]=>:~, [false, true]=>:ILIKE, [false, false]=>:LIKE}
@@ -949,6 +1387,11 @@ module Sequel
949
1387
  end
950
1388
  end
951
1389
  private_class_method :like_element
1390
+
1391
+ # Return self instead of creating a new object to save on memory.
1392
+ def sql_string
1393
+ self
1394
+ end
952
1395
  end
953
1396
 
954
1397
  # Represents an SQL array access, with multiple possible arguments.
@@ -966,9 +1409,21 @@ module Sequel
966
1409
 
967
1410
  # Create a new +Subscript+ appending the given subscript(s)
968
1411
  # the the current array of subscripts.
1412
+ #
1413
+ # :a.sql_subscript(2) # a[2]
1414
+ # :a.sql_subscript(2) | 1 # a[2, 1]
969
1415
  def |(sub)
970
1416
  Subscript.new(@f, @sub + Array(sub))
971
1417
  end
1418
+
1419
+ # Create a new +Subscript+ by accessing a subarray of a multidimensional
1420
+ # array.
1421
+ #
1422
+ # :a.sql_subscript(2) # a[2]
1423
+ # :a.sql_subscript(2)[1] # a[2][1]
1424
+ def [](sub)
1425
+ Subscript.new(self, Array(sub))
1426
+ end
972
1427
 
973
1428
  to_s_method :subscript_sql
974
1429
  end
@@ -1019,28 +1474,71 @@ module Sequel
1019
1474
  # Examples:
1020
1475
  #
1021
1476
  # ds = DB[:t]
1477
+ #
1022
1478
  # # Argument yielded to block
1023
1479
  # ds.filter{|r| r.name < 2} # SELECT * FROM t WHERE (name < 2)
1480
+ #
1024
1481
  # # Block without argument (instance_eval)
1025
1482
  # ds.filter{name < 2} # SELECT * FROM t WHERE (name < 2)
1483
+ #
1026
1484
  # # Qualified identifiers
1027
1485
  # ds.filter{table__column + 1 < 2} # SELECT * FROM t WHERE ((table.column + 1) < 2)
1486
+ #
1028
1487
  # # Functions
1029
1488
  # ds.filter{is_active(1, 'arg2')} # SELECT * FROM t WHERE is_active(1, 'arg2')
1030
1489
  # ds.select{version{}} # SELECT version() FROM t
1031
1490
  # ds.select{count(:*){}} # SELECT count(*) FROM t
1032
1491
  # ds.select{count(:distinct, col1){}} # SELECT count(DISTINCT col1) FROM t
1492
+ #
1033
1493
  # # Window Functions
1034
1494
  # ds.select{rank(:over){}} # SELECT rank() OVER () FROM t
1035
1495
  # ds.select{count(:over, :*=>true){}} # SELECT count(*) OVER () FROM t
1036
1496
  # ds.select{sum(:over, :args=>col1, :partition=>col2, :order=>col3){}} # SELECT sum(col1) OVER (PARTITION BY col2 ORDER BY col3) FROM t
1037
1497
  #
1498
+ # # Math Operators
1499
+ # ds.select{|o| o.+(1, :a).as(:b)} # SELECT (1 + a) AS b FROM t
1500
+ # ds.select{|o| o.-(2, :a).as(:b)} # SELECT (2 - a) AS b FROM t
1501
+ # ds.select{|o| o.*(3, :a).as(:b)} # SELECT (3 * a) AS b FROM t
1502
+ # ds.select{|o| o./(4, :a).as(:b)} # SELECT (4 / a) AS b FROM t
1503
+ #
1504
+ # # Boolean Operators
1505
+ # ds.filter{|o| o.&({:a=>1}, :b)} # SELECT * FROM t WHERE ((a = 1) AND b)
1506
+ # ds.filter{|o| o.|({:a=>1}, :b)} # SELECT * FROM t WHERE ((a = 1) OR b)
1507
+ # ds.filter{|o| o.~({:a=>1})} # SELECT * FROM t WHERE (a != 1)
1508
+ # ds.filter{|o| o.~({:a=>1, :b=>2})} # SELECT * FROM t WHERE ((a != 1) OR (b != 2))
1509
+ #
1510
+ # # Inequality Operators
1511
+ # ds.filter{|o| o.>(1, :a)} # SELECT * FROM t WHERE (1 > a)
1512
+ # ds.filter{|o| o.<(2, :a)} # SELECT * FROM t WHERE (2 < a)
1513
+ # ds.filter{|o| o.>=(3, :a)} # SELECT * FROM t WHERE (3 >= a)
1514
+ # ds.filter{|o| o.<=(4, :a)} # SELECT * FROM t WHERE (4 <= a)
1515
+ #
1516
+ # # Literal Strings
1517
+ # ds.filter{{a=>`some SQL`}} # SELECT * FROM t WHERE (a = some SQL)
1518
+ #
1038
1519
  # For a more detailed explanation, see the {Virtual Rows guide}[link:files/doc/virtual_rows_rdoc.html].
1039
1520
  class VirtualRow < BasicObject
1040
1521
  WILDCARD = LiteralString.new('*').freeze
1041
1522
  QUESTION_MARK = LiteralString.new('?').freeze
1042
1523
  COMMA_SEPARATOR = LiteralString.new(', ').freeze
1043
1524
  DOUBLE_UNDERSCORE = '__'.freeze
1525
+ DISTINCT = ["DISTINCT ".freeze].freeze
1526
+ COMMA_ARRAY = [COMMA_SEPARATOR].freeze
1527
+
1528
+ include OperatorBuilders
1529
+
1530
+ %w'> < >= <='.each do |op|
1531
+ class_eval(<<-END, __FILE__, __LINE__ + 1)
1532
+ def #{op}(*args)
1533
+ SQL::BooleanExpression.new(:#{op}, *args)
1534
+ end
1535
+ END
1536
+ end
1537
+
1538
+ # Return a literal string created with the given string.
1539
+ def `(s)
1540
+ Sequel::LiteralString.new(s)
1541
+ end
1044
1542
 
1045
1543
  # Return an +Identifier+, +QualifiedIdentifier+, +Function+, or +WindowFunction+, depending
1046
1544
  # on arguments and whether a block is provided. Does not currently call the block.
@@ -1054,7 +1552,7 @@ module Sequel
1054
1552
  when :*
1055
1553
  Function.new(m, WILDCARD)
1056
1554
  when :distinct
1057
- Function.new(m, PlaceholderLiteralString.new("DISTINCT #{args.map{QUESTION_MARK}.join(COMMA_SEPARATOR)}", args))
1555
+ Function.new(m, PlaceholderLiteralString.new(DISTINCT + COMMA_ARRAY * (args.length-1), args))
1058
1556
  when :over
1059
1557
  opts = args.shift || {}
1060
1558
  fun_args = ::Kernel.Array(opts[:*] ? WILDCARD : opts[:args])
@@ -1108,6 +1606,20 @@ module Sequel
1108
1606
 
1109
1607
  to_s_method :window_function_sql, '@function, @window'
1110
1608
  end
1609
+
1610
+ # A +Wrapper+ is a simple way to wrap an existing object so that it supports
1611
+ # the Sequel DSL.
1612
+ class Wrapper < GenericExpression
1613
+ # The underlying value wrapped by this object.
1614
+ attr_reader :value
1615
+
1616
+ # Set the value wrapped by the object.
1617
+ def initialize(value)
1618
+ @value = value
1619
+ end
1620
+
1621
+ to_s_method :literal, '@value'
1622
+ end
1111
1623
  end
1112
1624
 
1113
1625
  # +LiteralString+ is used to represent literal SQL expressions. A
@@ -1120,7 +1632,22 @@ module Sequel
1120
1632
  include SQL::NumericMethods
1121
1633
  include SQL::StringMethods
1122
1634
  include SQL::InequalityMethods
1635
+ include SQL::AliasMethods
1636
+ include SQL::CastMethods
1637
+
1638
+ # Return self if no args are given, otherwise return a SQL::PlaceholderLiteralString
1639
+ # with the current string and the given args.
1640
+ def lit(*args)
1641
+ args.empty? ? self : SQL::PlaceholderLiteralString.new(self, args)
1642
+ end
1643
+
1644
+ # Convert a literal string to a SQL::Blob.
1645
+ def to_sequel_blob
1646
+ SQL::Blob.new(self)
1647
+ end
1123
1648
  end
1124
1649
 
1125
1650
  include SQL::Constants
1651
+ extend SQL::Builders
1652
+ extend SQL::OperatorBuilders
1126
1653
  end