sequel 3.21.0 → 3.36.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 (311) hide show
  1. data/CHANGELOG +855 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +23 -7
  4. data/Rakefile +42 -24
  5. data/bin/sequel +26 -8
  6. data/doc/active_record.rdoc +1 -0
  7. data/doc/advanced_associations.rdoc +171 -11
  8. data/doc/association_basics.rdoc +293 -43
  9. data/doc/dataset_basics.rdoc +5 -5
  10. data/doc/dataset_filtering.rdoc +8 -0
  11. data/doc/mass_assignment.rdoc +56 -0
  12. data/doc/migration.rdoc +85 -552
  13. data/doc/model_hooks.rdoc +78 -27
  14. data/doc/object_model.rdoc +541 -0
  15. data/doc/opening_databases.rdoc +97 -50
  16. data/doc/prepared_statements.rdoc +40 -14
  17. data/doc/querying.rdoc +28 -4
  18. data/doc/reflection.rdoc +30 -4
  19. data/doc/release_notes/3.22.0.txt +39 -0
  20. data/doc/release_notes/3.23.0.txt +172 -0
  21. data/doc/release_notes/3.24.0.txt +420 -0
  22. data/doc/release_notes/3.25.0.txt +88 -0
  23. data/doc/release_notes/3.26.0.txt +88 -0
  24. data/doc/release_notes/3.27.0.txt +82 -0
  25. data/doc/release_notes/3.28.0.txt +304 -0
  26. data/doc/release_notes/3.29.0.txt +459 -0
  27. data/doc/release_notes/3.30.0.txt +135 -0
  28. data/doc/release_notes/3.31.0.txt +146 -0
  29. data/doc/release_notes/3.32.0.txt +202 -0
  30. data/doc/release_notes/3.33.0.txt +157 -0
  31. data/doc/release_notes/3.34.0.txt +671 -0
  32. data/doc/release_notes/3.35.0.txt +144 -0
  33. data/doc/release_notes/3.36.0.txt +245 -0
  34. data/doc/schema_modification.rdoc +572 -0
  35. data/doc/sharding.rdoc +95 -3
  36. data/doc/testing.rdoc +129 -0
  37. data/doc/thread_safety.rdoc +17 -0
  38. data/doc/transactions.rdoc +137 -0
  39. data/doc/virtual_rows.rdoc +51 -2
  40. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  41. data/lib/sequel/adapters/ado.rb +40 -18
  42. data/lib/sequel/adapters/amalgalite.rb +17 -9
  43. data/lib/sequel/adapters/db2.rb +175 -86
  44. data/lib/sequel/adapters/dbi.rb +15 -15
  45. data/lib/sequel/adapters/do/mysql.rb +17 -11
  46. data/lib/sequel/adapters/do/postgres.rb +1 -57
  47. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  48. data/lib/sequel/adapters/do.rb +18 -36
  49. data/lib/sequel/adapters/firebird.rb +27 -208
  50. data/lib/sequel/adapters/ibmdb.rb +453 -0
  51. data/lib/sequel/adapters/informix.rb +6 -23
  52. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  53. data/lib/sequel/adapters/jdbc/db2.rb +47 -0
  54. data/lib/sequel/adapters/jdbc/derby.rb +287 -0
  55. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  56. data/lib/sequel/adapters/jdbc/h2.rb +73 -31
  57. data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
  58. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  59. data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
  60. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  61. data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
  62. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  63. data/lib/sequel/adapters/jdbc/postgresql.rb +58 -48
  64. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  65. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  66. data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
  67. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  68. data/lib/sequel/adapters/jdbc.rb +265 -94
  69. data/lib/sequel/adapters/mock.rb +361 -0
  70. data/lib/sequel/adapters/mysql.rb +118 -222
  71. data/lib/sequel/adapters/mysql2.rb +58 -57
  72. data/lib/sequel/adapters/odbc/mssql.rb +15 -7
  73. data/lib/sequel/adapters/odbc.rb +28 -9
  74. data/lib/sequel/adapters/openbase.rb +3 -5
  75. data/lib/sequel/adapters/oracle.rb +349 -53
  76. data/lib/sequel/adapters/postgres.rb +356 -107
  77. data/lib/sequel/adapters/shared/access.rb +32 -9
  78. data/lib/sequel/adapters/shared/db2.rb +342 -0
  79. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  80. data/lib/sequel/adapters/shared/informix.rb +53 -0
  81. data/lib/sequel/adapters/shared/mssql.rb +335 -118
  82. data/lib/sequel/adapters/shared/mysql.rb +340 -76
  83. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
  84. data/lib/sequel/adapters/shared/oracle.rb +243 -45
  85. data/lib/sequel/adapters/shared/postgres.rb +524 -279
  86. data/lib/sequel/adapters/shared/progress.rb +5 -8
  87. data/lib/sequel/adapters/shared/sqlite.rb +265 -89
  88. data/lib/sequel/adapters/sqlite.rb +101 -39
  89. data/lib/sequel/adapters/swift/mysql.rb +9 -12
  90. data/lib/sequel/adapters/swift/postgres.rb +9 -74
  91. data/lib/sequel/adapters/swift/sqlite.rb +8 -6
  92. data/lib/sequel/adapters/swift.rb +8 -7
  93. data/lib/sequel/adapters/tinytds.rb +161 -21
  94. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
  95. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  96. data/lib/sequel/ast_transformer.rb +194 -0
  97. data/lib/sequel/connection_pool/sharded_single.rb +11 -1
  98. data/lib/sequel/connection_pool/sharded_threaded.rb +28 -2
  99. data/lib/sequel/connection_pool/single.rb +5 -0
  100. data/lib/sequel/connection_pool/threaded.rb +38 -14
  101. data/lib/sequel/connection_pool.rb +10 -2
  102. data/lib/sequel/core.rb +118 -11
  103. data/lib/sequel/database/connecting.rb +38 -28
  104. data/lib/sequel/database/dataset.rb +4 -4
  105. data/lib/sequel/database/dataset_defaults.rb +63 -2
  106. data/lib/sequel/database/logging.rb +7 -2
  107. data/lib/sequel/database/misc.rb +150 -46
  108. data/lib/sequel/database/query.rb +215 -65
  109. data/lib/sequel/database/schema_generator.rb +64 -20
  110. data/lib/sequel/database/schema_methods.rb +212 -54
  111. data/lib/sequel/dataset/actions.rb +322 -85
  112. data/lib/sequel/dataset/features.rb +96 -8
  113. data/lib/sequel/dataset/graph.rb +38 -11
  114. data/lib/sequel/dataset/misc.rb +50 -23
  115. data/lib/sequel/dataset/mutation.rb +12 -7
  116. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  117. data/lib/sequel/dataset/query.rb +237 -44
  118. data/lib/sequel/dataset/sql.rb +698 -278
  119. data/lib/sequel/dataset.rb +8 -0
  120. data/lib/sequel/exceptions.rb +4 -0
  121. data/lib/sequel/extensions/_pretty_table.rb +79 -0
  122. data/lib/sequel/extensions/arbitrary_servers.rb +108 -0
  123. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  124. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +16 -34
  125. data/lib/sequel/extensions/migration.rb +97 -11
  126. data/lib/sequel/extensions/named_timezones.rb +5 -0
  127. data/lib/sequel/extensions/null_dataset.rb +90 -0
  128. data/lib/sequel/extensions/pg_array.rb +460 -0
  129. data/lib/sequel/extensions/pg_array_ops.rb +220 -0
  130. data/lib/sequel/extensions/pg_auto_parameterize.rb +169 -0
  131. data/lib/sequel/extensions/pg_hstore.rb +296 -0
  132. data/lib/sequel/extensions/pg_hstore_ops.rb +259 -0
  133. data/lib/sequel/extensions/pg_inet.rb +89 -0
  134. data/lib/sequel/extensions/pg_json.rb +178 -0
  135. data/lib/sequel/extensions/pg_statement_cache.rb +316 -0
  136. data/lib/sequel/extensions/pretty_table.rb +5 -71
  137. data/lib/sequel/extensions/query.rb +1 -1
  138. data/lib/sequel/extensions/query_literals.rb +79 -0
  139. data/lib/sequel/extensions/schema_caching.rb +76 -0
  140. data/lib/sequel/extensions/schema_dumper.rb +251 -37
  141. data/lib/sequel/extensions/select_remove.rb +35 -0
  142. data/lib/sequel/extensions/server_block.rb +139 -0
  143. data/lib/sequel/extensions/sql_expr.rb +4 -110
  144. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  145. data/lib/sequel/extensions/to_dot.rb +95 -83
  146. data/lib/sequel/model/associations.rb +1187 -393
  147. data/lib/sequel/model/base.rb +578 -170
  148. data/lib/sequel/model/errors.rb +1 -1
  149. data/lib/sequel/model/exceptions.rb +5 -1
  150. data/lib/sequel/model/inflections.rb +1 -1
  151. data/lib/sequel/model.rb +30 -11
  152. data/lib/sequel/no_core_ext.rb +2 -0
  153. data/lib/sequel/plugins/active_model.rb +13 -1
  154. data/lib/sequel/plugins/association_pks.rb +22 -4
  155. data/lib/sequel/plugins/caching.rb +25 -18
  156. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  157. data/lib/sequel/plugins/composition.rb +44 -12
  158. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  159. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  160. data/lib/sequel/plugins/dirty.rb +214 -0
  161. data/lib/sequel/plugins/eager_each.rb +59 -0
  162. data/lib/sequel/plugins/force_encoding.rb +6 -6
  163. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  164. data/lib/sequel/plugins/identity_map.rb +123 -8
  165. data/lib/sequel/plugins/instance_filters.rb +10 -0
  166. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  167. data/lib/sequel/plugins/json_serializer.rb +58 -6
  168. data/lib/sequel/plugins/list.rb +12 -2
  169. data/lib/sequel/plugins/many_through_many.rb +79 -28
  170. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  171. data/lib/sequel/plugins/nested_attributes.rb +139 -63
  172. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  173. data/lib/sequel/plugins/prepared_statements.rb +167 -0
  174. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  175. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  176. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  177. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  178. data/lib/sequel/plugins/schema.rb +7 -2
  179. data/lib/sequel/plugins/serialization.rb +98 -49
  180. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  181. data/lib/sequel/plugins/sharding.rb +21 -54
  182. data/lib/sequel/plugins/single_table_inheritance.rb +5 -3
  183. data/lib/sequel/plugins/static_cache.rb +99 -0
  184. data/lib/sequel/plugins/subclasses.rb +10 -2
  185. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  186. data/lib/sequel/plugins/timestamps.rb +1 -1
  187. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  188. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  189. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  190. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  191. data/lib/sequel/sql.rb +526 -59
  192. data/lib/sequel/timezones.rb +57 -38
  193. data/lib/sequel/version.rb +1 -1
  194. data/spec/adapters/db2_spec.rb +146 -0
  195. data/spec/adapters/firebird_spec.rb +1 -1
  196. data/spec/adapters/mssql_spec.rb +142 -40
  197. data/spec/adapters/mysql_spec.rb +380 -234
  198. data/spec/adapters/oracle_spec.rb +67 -77
  199. data/spec/adapters/postgres_spec.rb +1050 -212
  200. data/spec/adapters/spec_helper.rb +8 -5
  201. data/spec/adapters/sqlite_spec.rb +139 -27
  202. data/spec/core/connection_pool_spec.rb +196 -93
  203. data/spec/core/database_spec.rb +879 -510
  204. data/spec/core/dataset_spec.rb +1415 -1086
  205. data/spec/core/expression_filters_spec.rb +554 -109
  206. data/spec/core/mock_adapter_spec.rb +449 -0
  207. data/spec/core/object_graph_spec.rb +86 -114
  208. data/spec/core/schema_generator_spec.rb +4 -4
  209. data/spec/core/schema_spec.rb +482 -38
  210. data/spec/core/spec_helper.rb +6 -48
  211. data/spec/extensions/active_model_spec.rb +13 -0
  212. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  213. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  214. data/spec/extensions/association_dependencies_spec.rb +15 -25
  215. data/spec/extensions/association_pks_spec.rb +66 -32
  216. data/spec/extensions/association_proxies_spec.rb +4 -4
  217. data/spec/extensions/caching_spec.rb +47 -51
  218. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  219. data/spec/extensions/columns_introspection_spec.rb +91 -0
  220. data/spec/extensions/composition_spec.rb +18 -13
  221. data/spec/{core/core_sql_spec.rb → extensions/core_extensions_spec.rb} +119 -139
  222. data/spec/extensions/dataset_associations_spec.rb +199 -0
  223. data/spec/extensions/defaults_setter_spec.rb +64 -0
  224. data/spec/extensions/dirty_spec.rb +155 -0
  225. data/spec/extensions/eager_each_spec.rb +34 -0
  226. data/spec/extensions/force_encoding_spec.rb +4 -2
  227. data/spec/extensions/hook_class_methods_spec.rb +69 -92
  228. data/spec/extensions/identity_map_spec.rb +131 -22
  229. data/spec/extensions/inflector_spec.rb +0 -4
  230. data/spec/extensions/instance_filters_spec.rb +11 -21
  231. data/spec/extensions/instance_hooks_spec.rb +72 -0
  232. data/spec/extensions/json_serializer_spec.rb +49 -6
  233. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  234. data/spec/extensions/list_spec.rb +48 -39
  235. data/spec/extensions/many_through_many_spec.rb +283 -250
  236. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  237. data/spec/extensions/migration_spec.rb +140 -35
  238. data/spec/extensions/named_timezones_spec.rb +29 -11
  239. data/spec/extensions/nested_attributes_spec.rb +251 -89
  240. data/spec/extensions/null_dataset_spec.rb +85 -0
  241. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  242. data/spec/extensions/pg_array_ops_spec.rb +105 -0
  243. data/spec/extensions/pg_array_spec.rb +196 -0
  244. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  245. data/spec/extensions/pg_hstore_ops_spec.rb +136 -0
  246. data/spec/extensions/pg_hstore_spec.rb +195 -0
  247. data/spec/extensions/pg_inet_spec.rb +44 -0
  248. data/spec/extensions/pg_json_spec.rb +101 -0
  249. data/spec/extensions/pg_statement_cache_spec.rb +209 -0
  250. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  251. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  252. data/spec/extensions/prepared_statements_spec.rb +87 -0
  253. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  254. data/spec/extensions/pretty_table_spec.rb +7 -6
  255. data/spec/extensions/query_literals_spec.rb +168 -0
  256. data/spec/extensions/query_spec.rb +6 -0
  257. data/spec/extensions/rcte_tree_spec.rb +50 -43
  258. data/spec/extensions/schema_caching_spec.rb +41 -0
  259. data/spec/extensions/schema_dumper_spec.rb +429 -22
  260. data/spec/extensions/schema_spec.rb +25 -21
  261. data/spec/extensions/select_remove_spec.rb +38 -0
  262. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  263. data/spec/extensions/serialization_spec.rb +85 -43
  264. data/spec/extensions/server_block_spec.rb +90 -0
  265. data/spec/extensions/sharding_spec.rb +69 -143
  266. data/spec/extensions/single_table_inheritance_spec.rb +23 -29
  267. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  268. data/spec/extensions/spec_helper.rb +22 -61
  269. data/spec/extensions/sql_expr_spec.rb +29 -60
  270. data/spec/extensions/static_cache_spec.rb +145 -0
  271. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  272. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  273. data/spec/extensions/timestamps_spec.rb +6 -6
  274. data/spec/extensions/to_dot_spec.rb +4 -7
  275. data/spec/extensions/touch_spec.rb +13 -14
  276. data/spec/extensions/tree_spec.rb +11 -26
  277. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  278. data/spec/extensions/update_primary_key_spec.rb +30 -24
  279. data/spec/extensions/validation_class_methods_spec.rb +30 -51
  280. data/spec/extensions/validation_helpers_spec.rb +16 -35
  281. data/spec/extensions/xml_serializer_spec.rb +29 -4
  282. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  283. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  284. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  285. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  286. data/spec/integration/associations_test.rb +659 -21
  287. data/spec/integration/database_test.rb +9 -3
  288. data/spec/integration/dataset_test.rb +605 -91
  289. data/spec/integration/eager_loader_test.rb +19 -21
  290. data/spec/integration/migrator_test.rb +2 -3
  291. data/spec/integration/model_test.rb +81 -2
  292. data/spec/integration/plugin_test.rb +454 -143
  293. data/spec/integration/prepared_statement_test.rb +263 -128
  294. data/spec/integration/schema_test.rb +309 -43
  295. data/spec/integration/spec_helper.rb +40 -30
  296. data/spec/integration/timezone_test.rb +38 -12
  297. data/spec/integration/transaction_test.rb +183 -8
  298. data/spec/integration/type_test.rb +18 -4
  299. data/spec/model/association_reflection_spec.rb +201 -7
  300. data/spec/model/associations_spec.rb +1067 -661
  301. data/spec/model/base_spec.rb +329 -95
  302. data/spec/model/dataset_methods_spec.rb +7 -27
  303. data/spec/model/eager_loading_spec.rb +677 -676
  304. data/spec/model/hooks_spec.rb +349 -67
  305. data/spec/model/model_spec.rb +240 -168
  306. data/spec/model/plugins_spec.rb +24 -13
  307. data/spec/model/record_spec.rb +490 -232
  308. data/spec/model/spec_helper.rb +14 -71
  309. data/spec/model/validations_spec.rb +11 -0
  310. data/spec/rcov.opts +2 -0
  311. metadata +178 -80
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.
@@ -69,7 +79,8 @@ module Sequel
69
79
  # the method provided on the dataset with args as the argument (self by default).
70
80
  # Used to DRY up some code.
71
81
  def self.to_s_method(meth, args=:self) # :nodoc:
72
- class_eval("def to_s(ds); ds.#{meth}(#{args}) end", __FILE__, __LINE__)
82
+ class_eval("def to_s(ds) ds.#{meth}(#{args}) end", __FILE__, __LINE__)
83
+ class_eval("def to_s_append(ds, sql) ds.#{meth}_append(sql, #{args}) end", __FILE__, __LINE__)
73
84
  end
74
85
  private_class_method :to_s_method
75
86
 
@@ -127,7 +138,7 @@ module Sequel
127
138
  MATHEMATICAL_OPERATORS = [:+, :-, :/, :*]
128
139
 
129
140
  # Bitwise mathematical operators used in +NumericMethods+
130
- BITWISE_OPERATORS = [:&, :|, :^, :<<, :>>]
141
+ BITWISE_OPERATORS = [:&, :|, :^, :<<, :>>, :%]
131
142
 
132
143
  # Inequality operators used in +InequalityMethods+
133
144
  INEQUALITY_OPERATORS = [:<, :>, :<=, :>=]
@@ -144,14 +155,17 @@ module Sequel
144
155
  # Operator symbols that take exactly two arguments
145
156
  TWO_ARITY_OPERATORS = [:'=', :'!=', :LIKE, :'NOT LIKE', \
146
157
  :~, :'!~', :'~*', :'!~*', :ILIKE, :'NOT ILIKE'] + \
147
- INEQUALITY_OPERATORS + BITWISE_OPERATORS + IS_OPERATORS + IN_OPERATORS
158
+ INEQUALITY_OPERATORS + IS_OPERATORS + IN_OPERATORS
148
159
 
149
160
  # Operator symbols that take one or more arguments
150
- N_ARITY_OPERATORS = [:AND, :OR, :'||'] + MATHEMATICAL_OPERATORS
161
+ N_ARITY_OPERATORS = [:AND, :OR, :'||'] + MATHEMATICAL_OPERATORS + BITWISE_OPERATORS
151
162
 
152
163
  # Operator symbols that take only a single argument
153
164
  ONE_ARITY_OPERATORS = [:NOT, :NOOP, :'B~']
154
165
 
166
+ # Custom expressions that may have different syntax on different databases
167
+ CUSTOM_EXPRESSIONS = [:extract]
168
+
155
169
  # An array of args for this object
156
170
  attr_reader :args
157
171
 
@@ -180,6 +194,8 @@ module Sequel
180
194
  args[1] = orig_args[1] if IN_OPERATORS.include?(op)
181
195
  when *ONE_ARITY_OPERATORS
182
196
  raise(Error, "The #{op} operator requires a single argument") unless args.length == 1
197
+ when *CUSTOM_EXPRESSIONS
198
+ # nothing
183
199
  else
184
200
  raise(Error, "Invalid operator #{op}")
185
201
  end
@@ -219,14 +235,7 @@ module Sequel
219
235
  # ~:a.sql_number # ~"a"
220
236
  module BitwiseMethods
221
237
  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
238
+ module_eval("def #{o}(o) NumericExpression.new(#{o.inspect}, self, o) end", __FILE__, __LINE__)
230
239
  end
231
240
 
232
241
  # Do the bitwise compliment of the self
@@ -244,16 +253,24 @@ module Sequel
244
253
  # :a & :b # "a" AND "b"
245
254
  # :a | :b # "a" OR "b"
246
255
  # ~:a # NOT "a"
256
+ #
257
+ # One exception to this is when a NumericExpression or Integer is the argument
258
+ # to & or |, in which case a bitwise method will be used:
259
+ #
260
+ # :a & 1 # "a" & 1
261
+ # :a | (:b + 1) # "a" | ("b" + 1)
247
262
  module BooleanMethods
248
263
  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)
264
+ module_eval(<<-END, __FILE__, __LINE__+1)
265
+ def #{m}(o)
266
+ case o
267
+ when NumericExpression, Integer
268
+ NumericExpression.new(#{m.inspect}, self, o)
269
+ else
270
+ BooleanExpression.new(#{o.inspect}, self, o)
271
+ end
255
272
  end
256
- end
273
+ END
257
274
  end
258
275
 
259
276
  # Create a new BooleanExpression with NOT, representing the inversion of whatever self represents.
@@ -264,6 +281,293 @@ module Sequel
264
281
  end
265
282
  end
266
283
 
284
+ # These methods are designed as replacements for the core extensions, so that
285
+ # Sequel is still easy to use if the core extensions are not enabled.
286
+ module Builders
287
+ # Create an SQL::AliasedExpression for the given expression and alias.
288
+ #
289
+ # Sequel.as(:column, :alias) # "column" AS "alias"
290
+ def as(exp, aliaz)
291
+ SQL::AliasedExpression.new(exp, aliaz)
292
+ end
293
+
294
+ # Order the given argument ascending.
295
+ # Options:
296
+ #
297
+ # :nulls :: Set to :first to use NULLS FIRST (so NULL values are ordered
298
+ # before other values), or :last to use NULLS LAST (so NULL values
299
+ # are ordered after other values).
300
+ #
301
+ # Sequel.asc(:a) # a ASC
302
+ # Sequel.asc(:b, :nulls=>:last) # b ASC NULLS LAST
303
+ def asc(arg, opts={})
304
+ SQL::OrderedExpression.new(arg, false, opts)
305
+ end
306
+
307
+ # Return an <tt>SQL::Blob</tt> that holds the same data as this string.
308
+ # Blobs provide proper escaping of binary data. If given a blob, returns it
309
+ # directly.
310
+ def blob(s)
311
+ if s.is_a?(SQL::Blob)
312
+ s
313
+ else
314
+ SQL::Blob.new(s)
315
+ end
316
+ end
317
+
318
+ # Return an <tt>SQL::CaseExpression</tt> created with the given arguments.
319
+ #
320
+ # Sequel.case([[{:a=>[2,3]}, 1]], 0) # SQL: CASE WHEN a IN (2, 3) THEN 1 ELSE 0 END
321
+ # Sequel.case({:a=>1}, 0, :b) # SQL: CASE b WHEN a THEN 1 ELSE 0 END
322
+ def case(*args) # core_sql ignore
323
+ SQL::CaseExpression.new(*args)
324
+ end
325
+
326
+ # Cast the reciever to the given SQL type. You can specify a ruby class as a type,
327
+ # and it is handled similarly to using a database independent type in the schema methods.
328
+ #
329
+ # Sequel.cast(:a, :integer) # CAST(a AS integer)
330
+ # Sequel.cast(:a, String) # CAST(a AS varchar(255))
331
+ def cast(arg, sql_type)
332
+ SQL::Cast.new(arg, sql_type)
333
+ end
334
+
335
+ # Cast the reciever to the given SQL type (or the database's default Integer type if none given),
336
+ # and return the result as a +NumericExpression+, so you can use the bitwise operators
337
+ # on the result.
338
+ #
339
+ # Sequel.cast_numeric(:a) # CAST(a AS integer)
340
+ # Sequel.cast_numeric(:a, Float) # CAST(a AS double precision)
341
+ def cast_numeric(arg, sql_type = nil)
342
+ cast(arg, sql_type || Integer).sql_number
343
+ end
344
+
345
+ # Cast the reciever to the given SQL type (or the database's default String type if none given),
346
+ # and return the result as a +StringExpression+, so you can use +
347
+ # directly on the result for SQL string concatenation.
348
+ #
349
+ # Sequel.cast_string(:a) # CAST(a AS varchar(255))
350
+ # Sequel.cast_string(:a, :text) # CAST(a AS text)
351
+ def cast_string(arg, sql_type = nil)
352
+ cast(arg, sql_type || String).sql_string
353
+ end
354
+
355
+ # Order the given argument descending.
356
+ # Options:
357
+ #
358
+ # :nulls :: Set to :first to use NULLS FIRST (so NULL values are ordered
359
+ # before other values), or :last to use NULLS LAST (so NULL values
360
+ # are ordered after other values).
361
+ #
362
+ # Sequel.desc(:a) # b DESC
363
+ # Sequel.desc(:b, :nulls=>:first) # b DESC NULLS FIRST
364
+ def desc(arg, opts={})
365
+ SQL::OrderedExpression.new(arg, true, opts)
366
+ end
367
+
368
+ # Wraps the given object in an appropriate Sequel wrapper.
369
+ # If the given object is already a Sequel object, return it directly.
370
+ # For condition specifiers (hashes and arrays of two pairs), true, and false,
371
+ # return a boolean expressions. For numeric objects, return a numeric
372
+ # expression. For strings, return a string expression. For procs or when
373
+ # the method is passed a block, evaluate it as a virtual row and wrap it
374
+ # appropriately. In all other cases, use a generic wrapper.
375
+ #
376
+ # This method allows you to construct SQL expressions that are difficult
377
+ # to construct via other methods. For example:
378
+ #
379
+ # Sequel.expr(1) - :a # SQL: (1 - a)
380
+ def expr(arg=(no_arg=true), &block)
381
+ if block_given?
382
+ if no_arg
383
+ return expr(block)
384
+ else
385
+ raise Error, 'cannot provide both an argument and a block to Sequel.expr'
386
+ end
387
+ elsif no_arg
388
+ raise Error, 'must provide either an argument or a block to Sequel.expr'
389
+ end
390
+
391
+ case arg
392
+ when SQL::Expression, LiteralString, SQL::Blob
393
+ arg
394
+ when Hash
395
+ SQL::BooleanExpression.from_value_pairs(arg, :AND)
396
+ when Array
397
+ if condition_specifier?(arg)
398
+ SQL::BooleanExpression.from_value_pairs(arg, :AND)
399
+ else
400
+ SQL::Wrapper.new(arg)
401
+ end
402
+ when Numeric
403
+ SQL::NumericExpression.new(:NOOP, arg)
404
+ when String
405
+ SQL::StringExpression.new(:NOOP, arg)
406
+ when TrueClass, FalseClass
407
+ SQL::BooleanExpression.new(:NOOP, arg)
408
+ when Proc
409
+ expr(virtual_row(&arg))
410
+ else
411
+ SQL::Wrapper.new(arg)
412
+ end
413
+ end
414
+
415
+ # Extract a datetime_part (e.g. year, month) from the given
416
+ # expression:
417
+ #
418
+ # Sequel.extract(:year, :date) # extract(year FROM "date")
419
+ def extract(datetime_part, exp)
420
+ SQL::NumericExpression.new(:extract, datetime_part, exp)
421
+ end
422
+
423
+ # Returns a <tt>Sequel::SQL::Function</tt> with the function name
424
+ # and the given arguments.
425
+ #
426
+ # Sequel.function(:now) # SQL: now()
427
+ # Sequel.function(:substr, :a, 1) # SQL: substr(a, 1)
428
+ def function(name, *args)
429
+ SQL::Function.new(name, *args)
430
+ end
431
+
432
+ # Return the argument wrapped as an <tt>SQL::Identifier</tt>.
433
+ #
434
+ # Sequel.identifier(:a__b) # "a__b"
435
+ def identifier(name)
436
+ SQL::Identifier.new(name)
437
+ end
438
+
439
+ # Return a <tt>Sequel::SQL::StringExpression</tt> representing an SQL string made up of the
440
+ # concatenation of the given array's elements. If an argument is passed,
441
+ # it is used in between each element of the array in the SQL
442
+ # concatenation.
443
+ #
444
+ # Sequel.join([:a]) # SQL: a
445
+ # Sequel.join([:a, :b]) # SQL: a || b
446
+ # Sequel.join([:a, 'b']) # SQL: a || 'b'
447
+ # Sequel.join(['a', :b], ' ') # SQL: 'a' || ' ' || b
448
+ def join(args, joiner=nil)
449
+ raise Error, 'argument to Sequel.join must be an array' unless args.is_a?(Array)
450
+ if joiner
451
+ args = args.zip([joiner]*args.length).flatten
452
+ args.pop
453
+ end
454
+
455
+ return SQL::StringExpression.new(:NOOP, '') if args.empty?
456
+
457
+ args = args.map do |a|
458
+ case a
459
+ when Symbol, ::Sequel::SQL::Expression, ::Sequel::LiteralString, TrueClass, FalseClass, NilClass
460
+ a
461
+ else
462
+ a.to_s
463
+ end
464
+ end
465
+ SQL::StringExpression.new(:'||', *args)
466
+ end
467
+
468
+ # Create a <tt>BooleanExpression</tt> case insensitive (if the database supports it) pattern match of the receiver with
469
+ # the given patterns. See <tt>SQL::StringExpression.like</tt>.
470
+ #
471
+ # Sequel.ilike(:a, 'A%') # "a" ILIKE 'A%'
472
+ def ilike(*args)
473
+ SQL::StringExpression.like(*(args << {:case_insensitive=>true}))
474
+ end
475
+
476
+ # Create a <tt>SQL::BooleanExpression</tt> case sensitive (if the database supports it) pattern match of the receiver with
477
+ # the given patterns. See <tt>SQL::StringExpression.like</tt>.
478
+ #
479
+ # Sequel.like(:a, 'A%') # "a" LIKE 'A%'
480
+ def like(*args)
481
+ SQL::StringExpression.like(*args)
482
+ end
483
+
484
+ # Converts a string into a <tt>Sequel::LiteralString</tt>, in order to override string
485
+ # literalization, e.g.:
486
+ #
487
+ # DB[:items].filter(:abc => 'def').sql #=>
488
+ # "SELECT * FROM items WHERE (abc = 'def')"
489
+ #
490
+ # DB[:items].filter(:abc => Sequel.lit('def')).sql #=>
491
+ # "SELECT * FROM items WHERE (abc = def)"
492
+ #
493
+ # You can also provide arguments, to create a <tt>Sequel::SQL::PlaceholderLiteralString</tt>:
494
+ #
495
+ # DB[:items].select{|o| o.count(Sequel.lit('DISTINCT ?', :a))}.sql #=>
496
+ # "SELECT count(DISTINCT a) FROM items"
497
+ def lit(s, *args) # core_sql ignore
498
+ if args.empty?
499
+ if s.is_a?(LiteralString)
500
+ s
501
+ else
502
+ LiteralString.new(s)
503
+ end
504
+ else
505
+ SQL::PlaceholderLiteralString.new(s, args)
506
+ end
507
+ end
508
+
509
+ # Return a <tt>Sequel::SQL::BooleanExpression</tt> created from the condition
510
+ # specifier, matching none of the conditions.
511
+ #
512
+ # Sequel.negate(:a=>true) # SQL: a IS NOT TRUE
513
+ # Sequel.negate([[:a, true]]) # SQL: a IS NOT TRUE
514
+ # Sequel.negate([[:a, 1], [:b, 2]]) # SQL: ((a != 1) AND (b != 2))
515
+ def negate(arg)
516
+ if condition_specifier?(arg)
517
+ SQL::BooleanExpression.from_value_pairs(arg, :AND, true)
518
+ else
519
+ raise Error, 'must pass a conditions specifier to Sequel.negate'
520
+ end
521
+ end
522
+
523
+ # Return a <tt>Sequel::SQL::BooleanExpression</tt> created from the condition
524
+ # specifier, matching any of the conditions.
525
+ #
526
+ # Sequel.or(:a=>true) # SQL: a IS TRUE
527
+ # Sequel.or([[:a, true]]) # SQL: a IS TRUE
528
+ # Sequel.or([[:a, 1], [:b, 2]]) # SQL: ((a = 1) OR (b = 2))
529
+ def or(arg)
530
+ if condition_specifier?(arg)
531
+ SQL::BooleanExpression.from_value_pairs(arg, :OR, false)
532
+ else
533
+ raise Error, 'must pass a conditions specifier to Sequel.or'
534
+ end
535
+ end
536
+
537
+ # Create a qualified identifier with the given qualifier and identifier
538
+ #
539
+ # Sequel.qualify(:table, :column) # "table"."column"
540
+ # Sequel.qualify(:schema, :table) # "schema"."table"
541
+ # Sequel.qualify(:table, :column).qualify(:schema) # "schema"."table"."column"
542
+ def qualify(qualifier, identifier)
543
+ SQL::QualifiedIdentifier.new(qualifier, identifier)
544
+ end
545
+
546
+ # Return an <tt>SQL::Subscript</tt> with the given arguments, representing an
547
+ # SQL array access.
548
+ #
549
+ # Sequel.subscript(:array, 1) # array[1]
550
+ # Sequel.subscript(:array, 1, 2) # array[1, 2]
551
+ # Sequel.subscript(:array, [1, 2]) # array[1, 2]
552
+ def subscript(exp, *subs)
553
+ SQL::Subscript.new(exp, subs.flatten)
554
+ end
555
+
556
+ # Return a <tt>SQL::ValueList</tt> created from the given array. Used if the array contains
557
+ # all two element arrays and you want it treated as an SQL value list (IN predicate)
558
+ # instead of as a conditions specifier (similar to a hash). This is not necessary if you are using
559
+ # this array as a value in a filter, but may be necessary if you are using it as a
560
+ # value with placeholder SQL:
561
+ #
562
+ # DB[:a].filter([:a, :b]=>[[1, 2], [3, 4]]) # SQL: (a, b) IN ((1, 2), (3, 4))
563
+ # DB[:a].filter('(a, b) IN ?', [[1, 2], [3, 4]]) # SQL: (a, b) IN ((1 = 2) AND (3 = 4))
564
+ # DB[:a].filter('(a, b) IN ?', Sequel.value_list([[1, 2], [3, 4]])) # SQL: (a, b) IN ((1, 2), (3, 4))
565
+ def value_list(arg)
566
+ raise Error, 'argument to Sequel.value_list must be an array' unless arg.is_a?(Array)
567
+ SQL::ValueList.new(arg)
568
+ end
569
+ end
570
+
267
571
  # Holds methods that are used to cast objects to different SQL types.
268
572
  module CastMethods
269
573
  # Cast the reciever to the given SQL type. You can specify a ruby class as a type,
@@ -316,12 +620,8 @@ module Sequel
316
620
  #
317
621
  # Also has the benefit of returning the result as a
318
622
  # 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
623
  def extract(datetime_part)
324
- Function.new(:extract, PlaceholderLiteralString.new("#{datetime_part} FROM ?", [self])).sql_number
624
+ NumericExpression.new(:extract, datetime_part, self)
325
625
  end
326
626
 
327
627
  # Return a BooleanExpression representation of +self+.
@@ -367,34 +667,12 @@ module Sequel
367
667
  # 'a'.lit <= :b # a <= "b"
368
668
  module InequalityMethods
369
669
  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
670
+ module_eval("def #{o}(o) BooleanExpression.new(#{o.inspect}, self, o) end", __FILE__, __LINE__)
378
671
  end
379
672
  end
380
673
 
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.
674
+ # Only exists for backwards compatibility, ignore it.
386
675
  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
676
  end
399
677
 
400
678
  # This module includes the standard mathematical methods (+, -, *, and /)
@@ -405,15 +683,66 @@ module Sequel
405
683
  # :a - :b # "a" - "b"
406
684
  # :a * :b # "a" * "b"
407
685
  # :a / :b # "a" / "b"
686
+ #
687
+ # One exception to this is if + is called with a +String+ or +StringExpression+,
688
+ # in which case the || operator is used instead of the + operator:
689
+ #
690
+ # :a + 'b' # "a" || 'b'
408
691
  module NumericMethods
409
692
  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)
693
+ module_eval("def #{o}(o) NumericExpression.new(#{o.inspect}, self, o) end", __FILE__, __LINE__) unless o == :+
694
+ end
695
+
696
+ # Use || as the operator when called with StringExpression and String instances,
697
+ # and the + operator for LiteralStrings and all other types.
698
+ def +(ce)
699
+ case ce
700
+ when LiteralString
701
+ NumericExpression.new(:+, self, ce)
702
+ when StringExpression, String
703
+ StringExpression.new(:'||', self, ce)
704
+ else
705
+ NumericExpression.new(:+, self, ce)
706
+ end
707
+ end
708
+ end
709
+
710
+ # These methods are designed as replacements for the core extension operator
711
+ # methods, so that Sequel is still easy to use if the core extensions are not
712
+ # enabled.
713
+ #
714
+ # The following methods are defined via metaprogramming: +, -, *, /, &, |.
715
+ # The +, -, *, and / operators return numeric expressions combining all the
716
+ # arguments with the appropriate operator, and the & and | operators return
717
+ # boolean expressions combining all of the arguments with either AND or OR.
718
+ module OperatorBuilders
719
+ %w'+ - * /'.each do |op|
720
+ class_eval(<<-END, __FILE__, __LINE__ + 1)
721
+ def #{op}(*args)
722
+ SQL::NumericExpression.new(:#{op}, *args)
723
+ end
724
+ END
725
+ end
726
+
727
+ {'&'=>'AND', '|'=>'OR'}.each do |m, op|
728
+ class_eval(<<-END, __FILE__, __LINE__ + 1)
729
+ def #{m}(*args)
730
+ SQL::BooleanExpression.new(:#{op}, *args)
416
731
  end
732
+ END
733
+ end
734
+
735
+ # Invert the given expression. Returns a <tt>Sequel::SQL::BooleanExpression</tt>
736
+ # created from this argument, not matching all of the conditions.
737
+ #
738
+ # Sequel.~(nil) # SQL: NOT NULL
739
+ # Sequel.~([[:a, true]]) # SQL: a IS NOT TRUE
740
+ # Sequel.~([[:a, 1], [:b, [2, 3]]]) # SQL: a != 1 OR b NOT IN (2, 3)
741
+ def ~(arg)
742
+ if condition_specifier?(arg)
743
+ SQL::BooleanExpression.from_value_pairs(arg, :OR, true)
744
+ else
745
+ SQL::BooleanExpression.invert(arg)
417
746
  end
418
747
  end
419
748
  end
@@ -595,10 +924,27 @@ module Sequel
595
924
  end
596
925
  when StringExpression, NumericExpression
597
926
  raise(Sequel::Error, "cannot invert #{ce.inspect}")
927
+ when Constant
928
+ CONSTANT_INVERSIONS[ce] || raise(Sequel::Error, "cannot invert #{ce.inspect}")
598
929
  else
599
930
  BooleanExpression.new(:NOT, ce)
600
931
  end
601
932
  end
933
+
934
+ # Always use an AND operator for & on BooleanExpressions
935
+ def &(ce)
936
+ BooleanExpression.new(:AND, self, ce)
937
+ end
938
+
939
+ # Always use an OR operator for | on BooleanExpressions
940
+ def |(ce)
941
+ BooleanExpression.new(:OR, self, ce)
942
+ end
943
+
944
+ # Return self instead of creating a new object to save on memory.
945
+ def sql_boolean
946
+ self
947
+ end
602
948
  end
603
949
 
604
950
  # Represents an SQL CASE expression, used for conditional branching in SQL.
@@ -666,6 +1012,21 @@ module Sequel
666
1012
  include CastMethods
667
1013
  include OrderMethods
668
1014
  include SubscriptMethods
1015
+
1016
+ # Return a BooleanExpression with the same op and args.
1017
+ def sql_boolean
1018
+ BooleanExpression.new(self.op, *self.args)
1019
+ end
1020
+
1021
+ # Return a NumericExpression with the same op and args.
1022
+ def sql_number
1023
+ NumericExpression.new(self.op, *self.args)
1024
+ end
1025
+
1026
+ # Return a StringExpression with the same op and args.
1027
+ def sql_string
1028
+ StringExpression.new(self.op, *self.args)
1029
+ end
669
1030
  end
670
1031
 
671
1032
  # Represents constants or psuedo-constants (e.g. +CURRENT_DATE+) in SQL.
@@ -706,6 +1067,12 @@ module Sequel
706
1067
  NOTNULL = NegativeBooleanConstant.new(nil)
707
1068
  end
708
1069
 
1070
+ class ComplexExpression
1071
+ # A hash of the opposite for each constant, used for inverting constants.
1072
+ CONSTANT_INVERSIONS = {Constants::TRUE=>Constants::FALSE, Constants::FALSE=>Constants::TRUE,
1073
+ Constants::NULL=>Constants::NOTNULL, Constants::NOTNULL=>Constants::NULL}
1074
+ end
1075
+
709
1076
  # Represents an SQL function call.
710
1077
  class Function < GenericExpression
711
1078
  # The array of arguments to pass to the function (may be blank)
@@ -812,7 +1179,8 @@ module Sequel
812
1179
  # substituted for :key phrases).
813
1180
  attr_reader :args
814
1181
 
815
- # The literal string containing placeholders
1182
+ # The literal string containing placeholders. This can also be an array
1183
+ # of strings, where each arg in args goes between the string elements.
816
1184
  attr_reader :str
817
1185
 
818
1186
  # Whether to surround the expression with parantheses
@@ -834,7 +1202,16 @@ module Sequel
834
1202
  include BitwiseMethods
835
1203
  include NumericMethods
836
1204
  include InequalityMethods
837
- include NoBooleanInputMethods
1205
+
1206
+ # Always use + for + operator for NumericExpressions.
1207
+ def +(ce)
1208
+ NumericExpression.new(:+, self, ce)
1209
+ end
1210
+
1211
+ # Return self instead of creating a new object to save on memory.
1212
+ def sql_number
1213
+ self
1214
+ end
838
1215
  end
839
1216
 
840
1217
  # Represents a column/expression to order the result set by.
@@ -891,7 +1268,7 @@ module Sequel
891
1268
  @table, @column = table, column
892
1269
  end
893
1270
 
894
- to_s_method :qualified_identifier_sql
1271
+ to_s_method :qualified_identifier_sql, "@table, @column"
895
1272
  end
896
1273
 
897
1274
  # Subclass of +ComplexExpression+ where the expression results
@@ -900,7 +1277,6 @@ module Sequel
900
1277
  include StringMethods
901
1278
  include StringConcatenationMethods
902
1279
  include InequalityMethods
903
- include NoBooleanInputMethods
904
1280
 
905
1281
  # Map of [regexp, case_insenstive] to +ComplexExpression+ operator symbol
906
1282
  LIKE_MAP = {[true, true]=>:'~*', [true, false]=>:~, [false, true]=>:ILIKE, [false, false]=>:LIKE}
@@ -949,6 +1325,11 @@ module Sequel
949
1325
  end
950
1326
  end
951
1327
  private_class_method :like_element
1328
+
1329
+ # Return self instead of creating a new object to save on memory.
1330
+ def sql_string
1331
+ self
1332
+ end
952
1333
  end
953
1334
 
954
1335
  # Represents an SQL array access, with multiple possible arguments.
@@ -966,9 +1347,21 @@ module Sequel
966
1347
 
967
1348
  # Create a new +Subscript+ appending the given subscript(s)
968
1349
  # the the current array of subscripts.
1350
+ #
1351
+ # :a.sql_subscript(2) # a[2]
1352
+ # :a.sql_subscript(2) | 1 # a[2, 1]
969
1353
  def |(sub)
970
1354
  Subscript.new(@f, @sub + Array(sub))
971
1355
  end
1356
+
1357
+ # Create a new +Subscript+ by accessing a subarray of a multidimensional
1358
+ # array.
1359
+ #
1360
+ # :a.sql_subscript(2) # a[2]
1361
+ # :a.sql_subscript(2)[1] # a[2][1]
1362
+ def [](sub)
1363
+ Subscript.new(self, Array(sub))
1364
+ end
972
1365
 
973
1366
  to_s_method :subscript_sql
974
1367
  end
@@ -1019,28 +1412,71 @@ module Sequel
1019
1412
  # Examples:
1020
1413
  #
1021
1414
  # ds = DB[:t]
1415
+ #
1022
1416
  # # Argument yielded to block
1023
1417
  # ds.filter{|r| r.name < 2} # SELECT * FROM t WHERE (name < 2)
1418
+ #
1024
1419
  # # Block without argument (instance_eval)
1025
1420
  # ds.filter{name < 2} # SELECT * FROM t WHERE (name < 2)
1421
+ #
1026
1422
  # # Qualified identifiers
1027
1423
  # ds.filter{table__column + 1 < 2} # SELECT * FROM t WHERE ((table.column + 1) < 2)
1424
+ #
1028
1425
  # # Functions
1029
1426
  # ds.filter{is_active(1, 'arg2')} # SELECT * FROM t WHERE is_active(1, 'arg2')
1030
1427
  # ds.select{version{}} # SELECT version() FROM t
1031
1428
  # ds.select{count(:*){}} # SELECT count(*) FROM t
1032
1429
  # ds.select{count(:distinct, col1){}} # SELECT count(DISTINCT col1) FROM t
1430
+ #
1033
1431
  # # Window Functions
1034
1432
  # ds.select{rank(:over){}} # SELECT rank() OVER () FROM t
1035
1433
  # ds.select{count(:over, :*=>true){}} # SELECT count(*) OVER () FROM t
1036
1434
  # ds.select{sum(:over, :args=>col1, :partition=>col2, :order=>col3){}} # SELECT sum(col1) OVER (PARTITION BY col2 ORDER BY col3) FROM t
1037
1435
  #
1436
+ # # Math Operators
1437
+ # ds.select{|o| o.+(1, :a).as(:b)} # SELECT (1 + a) AS b FROM t
1438
+ # ds.select{|o| o.-(2, :a).as(:b)} # SELECT (2 - a) AS b FROM t
1439
+ # ds.select{|o| o.*(3, :a).as(:b)} # SELECT (3 * a) AS b FROM t
1440
+ # ds.select{|o| o./(4, :a).as(:b)} # SELECT (4 / a) AS b FROM t
1441
+ #
1442
+ # # Boolean Operators
1443
+ # ds.filter{|o| o.&({:a=>1}, :b)} # SELECT * FROM t WHERE ((a = 1) AND b)
1444
+ # ds.filter{|o| o.|({:a=>1}, :b)} # SELECT * FROM t WHERE ((a = 1) OR b)
1445
+ # ds.filter{|o| o.~({:a=>1})} # SELECT * FROM t WHERE (a != 1)
1446
+ # ds.filter{|o| o.~({:a=>1, :b=>2})} # SELECT * FROM t WHERE ((a != 1) OR (b != 2))
1447
+ #
1448
+ # # Inequality Operators
1449
+ # ds.filter{|o| o.>(1, :a)} # SELECT * FROM t WHERE (1 > a)
1450
+ # ds.filter{|o| o.<(2, :a)} # SELECT * FROM t WHERE (2 < a)
1451
+ # ds.filter{|o| o.>=(3, :a)} # SELECT * FROM t WHERE (3 >= a)
1452
+ # ds.filter{|o| o.<=(4, :a)} # SELECT * FROM t WHERE (4 <= a)
1453
+ #
1454
+ # # Literal Strings
1455
+ # ds.filter{{a=>`some SQL`}} # SELECT * FROM t WHERE (a = some SQL)
1456
+ #
1038
1457
  # For a more detailed explanation, see the {Virtual Rows guide}[link:files/doc/virtual_rows_rdoc.html].
1039
1458
  class VirtualRow < BasicObject
1040
1459
  WILDCARD = LiteralString.new('*').freeze
1041
1460
  QUESTION_MARK = LiteralString.new('?').freeze
1042
1461
  COMMA_SEPARATOR = LiteralString.new(', ').freeze
1043
1462
  DOUBLE_UNDERSCORE = '__'.freeze
1463
+ DISTINCT = ["DISTINCT ".freeze].freeze
1464
+ COMMA_ARRAY = [COMMA_SEPARATOR].freeze
1465
+
1466
+ include OperatorBuilders
1467
+
1468
+ %w'> < >= <='.each do |op|
1469
+ class_eval(<<-END, __FILE__, __LINE__ + 1)
1470
+ def #{op}(*args)
1471
+ SQL::BooleanExpression.new(:#{op}, *args)
1472
+ end
1473
+ END
1474
+ end
1475
+
1476
+ # Return a literal string created with the given string.
1477
+ def `(s)
1478
+ Sequel::LiteralString.new(s)
1479
+ end
1044
1480
 
1045
1481
  # Return an +Identifier+, +QualifiedIdentifier+, +Function+, or +WindowFunction+, depending
1046
1482
  # on arguments and whether a block is provided. Does not currently call the block.
@@ -1054,7 +1490,7 @@ module Sequel
1054
1490
  when :*
1055
1491
  Function.new(m, WILDCARD)
1056
1492
  when :distinct
1057
- Function.new(m, PlaceholderLiteralString.new("DISTINCT #{args.map{QUESTION_MARK}.join(COMMA_SEPARATOR)}", args))
1493
+ Function.new(m, PlaceholderLiteralString.new(DISTINCT + COMMA_ARRAY * (args.length-1), args))
1058
1494
  when :over
1059
1495
  opts = args.shift || {}
1060
1496
  fun_args = ::Kernel.Array(opts[:*] ? WILDCARD : opts[:args])
@@ -1108,6 +1544,20 @@ module Sequel
1108
1544
 
1109
1545
  to_s_method :window_function_sql, '@function, @window'
1110
1546
  end
1547
+
1548
+ # A +Wrapper+ is a simple way to wrap an existing object so that it supports
1549
+ # the Sequel DSL.
1550
+ class Wrapper < GenericExpression
1551
+ # The underlying value wrapped by this object.
1552
+ attr_reader :value
1553
+
1554
+ # Set the value wrapped by the object.
1555
+ def initialize(value)
1556
+ @value = value
1557
+ end
1558
+
1559
+ to_s_method :literal, '@value'
1560
+ end
1111
1561
  end
1112
1562
 
1113
1563
  # +LiteralString+ is used to represent literal SQL expressions. A
@@ -1120,7 +1570,24 @@ module Sequel
1120
1570
  include SQL::NumericMethods
1121
1571
  include SQL::StringMethods
1122
1572
  include SQL::InequalityMethods
1573
+
1574
+ # If the core extensions are enabled, these will already be included
1575
+ # in String, so we don't need to include/define them here.
1576
+ unless Sequel.core_extensions?
1577
+ include Sequel::SQL::AliasMethods
1578
+ include Sequel::SQL::CastMethods
1579
+
1580
+ def lit(*args)
1581
+ args.empty? ? self : SQL::PlaceholderLiteralString.new(self, args)
1582
+ end
1583
+
1584
+ def to_sequel_blob
1585
+ SQL::Blob.new(self)
1586
+ end
1587
+ end
1123
1588
  end
1124
1589
 
1125
1590
  include SQL::Constants
1591
+ extend SQL::Builders
1592
+ extend SQL::OperatorBuilders
1126
1593
  end