sequel 3.21.0 → 3.31.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 (225) hide show
  1. data/CHANGELOG +485 -3
  2. data/README.rdoc +20 -6
  3. data/Rakefile +37 -19
  4. data/bin/sequel +1 -5
  5. data/doc/association_basics.rdoc +249 -43
  6. data/doc/dataset_basics.rdoc +4 -4
  7. data/doc/dataset_filtering.rdoc +8 -0
  8. data/doc/mass_assignment.rdoc +54 -0
  9. data/doc/migration.rdoc +15 -538
  10. data/doc/model_hooks.rdoc +72 -27
  11. data/doc/opening_databases.rdoc +89 -50
  12. data/doc/prepared_statements.rdoc +40 -13
  13. data/doc/reflection.rdoc +8 -2
  14. data/doc/release_notes/3.22.0.txt +39 -0
  15. data/doc/release_notes/3.23.0.txt +172 -0
  16. data/doc/release_notes/3.24.0.txt +420 -0
  17. data/doc/release_notes/3.25.0.txt +88 -0
  18. data/doc/release_notes/3.26.0.txt +88 -0
  19. data/doc/release_notes/3.27.0.txt +82 -0
  20. data/doc/release_notes/3.28.0.txt +304 -0
  21. data/doc/release_notes/3.29.0.txt +459 -0
  22. data/doc/release_notes/3.30.0.txt +135 -0
  23. data/doc/release_notes/3.31.0.txt +146 -0
  24. data/doc/schema_modification.rdoc +547 -0
  25. data/doc/sharding.rdoc +7 -1
  26. data/doc/testing.rdoc +129 -0
  27. data/doc/transactions.rdoc +137 -0
  28. data/doc/virtual_rows.rdoc +2 -2
  29. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  30. data/lib/sequel/adapters/ado.rb +40 -18
  31. data/lib/sequel/adapters/amalgalite.rb +17 -9
  32. data/lib/sequel/adapters/db2.rb +175 -86
  33. data/lib/sequel/adapters/dbi.rb +15 -15
  34. data/lib/sequel/adapters/do/mysql.rb +5 -7
  35. data/lib/sequel/adapters/do/postgres.rb +0 -5
  36. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  37. data/lib/sequel/adapters/do.rb +17 -36
  38. data/lib/sequel/adapters/firebird.rb +27 -208
  39. data/lib/sequel/adapters/ibmdb.rb +448 -0
  40. data/lib/sequel/adapters/informix.rb +6 -23
  41. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  42. data/lib/sequel/adapters/jdbc/db2.rb +45 -0
  43. data/lib/sequel/adapters/jdbc/derby.rb +247 -0
  44. data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
  45. data/lib/sequel/adapters/jdbc/h2.rb +56 -29
  46. data/lib/sequel/adapters/jdbc/hsqldb.rb +170 -0
  47. data/lib/sequel/adapters/jdbc/informix.rb +26 -0
  48. data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
  49. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  50. data/lib/sequel/adapters/jdbc/mysql.rb +11 -11
  51. data/lib/sequel/adapters/jdbc/oracle.rb +81 -25
  52. data/lib/sequel/adapters/jdbc/postgresql.rb +9 -13
  53. data/lib/sequel/adapters/jdbc/sqlite.rb +2 -6
  54. data/lib/sequel/adapters/jdbc/sqlserver.rb +71 -0
  55. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  56. data/lib/sequel/adapters/jdbc.rb +231 -78
  57. data/lib/sequel/adapters/mock.rb +335 -0
  58. data/lib/sequel/adapters/mysql.rb +82 -184
  59. data/lib/sequel/adapters/mysql2.rb +26 -25
  60. data/lib/sequel/adapters/odbc/db2.rb +17 -0
  61. data/lib/sequel/adapters/odbc/mssql.rb +1 -6
  62. data/lib/sequel/adapters/odbc.rb +28 -9
  63. data/lib/sequel/adapters/openbase.rb +3 -5
  64. data/lib/sequel/adapters/oracle.rb +351 -53
  65. data/lib/sequel/adapters/postgres.rb +194 -33
  66. data/lib/sequel/adapters/shared/access.rb +32 -9
  67. data/lib/sequel/adapters/shared/db2.rb +321 -0
  68. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  69. data/lib/sequel/adapters/shared/informix.rb +53 -0
  70. data/lib/sequel/adapters/shared/mssql.rb +231 -106
  71. data/lib/sequel/adapters/shared/mysql.rb +181 -63
  72. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
  73. data/lib/sequel/adapters/shared/oracle.rb +231 -41
  74. data/lib/sequel/adapters/shared/postgres.rb +214 -82
  75. data/lib/sequel/adapters/shared/progress.rb +4 -7
  76. data/lib/sequel/adapters/shared/sqlite.rb +201 -57
  77. data/lib/sequel/adapters/sqlite.rb +29 -21
  78. data/lib/sequel/adapters/swift/mysql.rb +12 -7
  79. data/lib/sequel/adapters/swift/postgres.rb +0 -5
  80. data/lib/sequel/adapters/swift/sqlite.rb +8 -6
  81. data/lib/sequel/adapters/swift.rb +5 -5
  82. data/lib/sequel/adapters/tinytds.rb +160 -19
  83. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
  84. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  85. data/lib/sequel/ast_transformer.rb +190 -0
  86. data/lib/sequel/connection_pool/threaded.rb +3 -2
  87. data/lib/sequel/connection_pool.rb +1 -1
  88. data/lib/sequel/core.rb +46 -5
  89. data/lib/sequel/database/connecting.rb +5 -6
  90. data/lib/sequel/database/dataset.rb +4 -4
  91. data/lib/sequel/database/dataset_defaults.rb +59 -1
  92. data/lib/sequel/database/logging.rb +1 -1
  93. data/lib/sequel/database/misc.rb +104 -19
  94. data/lib/sequel/database/query.rb +153 -58
  95. data/lib/sequel/database/schema_generator.rb +17 -7
  96. data/lib/sequel/database/schema_methods.rb +59 -29
  97. data/lib/sequel/dataset/actions.rb +231 -76
  98. data/lib/sequel/dataset/features.rb +91 -8
  99. data/lib/sequel/dataset/graph.rb +38 -11
  100. data/lib/sequel/dataset/misc.rb +50 -23
  101. data/lib/sequel/dataset/mutation.rb +5 -6
  102. data/lib/sequel/dataset/prepared_statements.rb +48 -27
  103. data/lib/sequel/dataset/query.rb +236 -43
  104. data/lib/sequel/dataset/sql.rb +665 -253
  105. data/lib/sequel/dataset.rb +8 -0
  106. data/lib/sequel/exceptions.rb +4 -0
  107. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  108. data/lib/sequel/extensions/migration.rb +6 -4
  109. data/lib/sequel/extensions/named_timezones.rb +5 -0
  110. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  111. data/lib/sequel/extensions/to_dot.rb +95 -83
  112. data/lib/sequel/model/associations.rb +1020 -337
  113. data/lib/sequel/model/base.rb +414 -132
  114. data/lib/sequel/model/errors.rb +1 -1
  115. data/lib/sequel/model/exceptions.rb +5 -1
  116. data/lib/sequel/model.rb +15 -8
  117. data/lib/sequel/plugins/association_pks.rb +22 -4
  118. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  119. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  120. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  121. data/lib/sequel/plugins/force_encoding.rb +6 -6
  122. data/lib/sequel/plugins/identity_map.rb +114 -7
  123. data/lib/sequel/plugins/many_through_many.rb +65 -7
  124. data/lib/sequel/plugins/prepared_statements.rb +151 -0
  125. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  126. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  127. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  128. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  129. data/lib/sequel/plugins/serialization.rb +88 -44
  130. data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
  131. data/lib/sequel/plugins/sharding.rb +21 -54
  132. data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
  133. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  134. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  135. data/lib/sequel/plugins/xml_serializer.rb +3 -3
  136. data/lib/sequel/sql.rb +109 -52
  137. data/lib/sequel/timezones.rb +57 -38
  138. data/lib/sequel/version.rb +1 -1
  139. data/spec/adapters/db2_spec.rb +146 -0
  140. data/spec/adapters/mssql_spec.rb +111 -29
  141. data/spec/adapters/mysql_spec.rb +135 -123
  142. data/spec/adapters/oracle_spec.rb +49 -77
  143. data/spec/adapters/postgres_spec.rb +267 -78
  144. data/spec/adapters/spec_helper.rb +6 -5
  145. data/spec/adapters/sqlite_spec.rb +35 -21
  146. data/spec/core/connection_pool_spec.rb +71 -92
  147. data/spec/core/core_sql_spec.rb +21 -30
  148. data/spec/core/database_spec.rb +788 -512
  149. data/spec/core/dataset_spec.rb +1073 -1053
  150. data/spec/core/expression_filters_spec.rb +159 -42
  151. data/spec/core/mock_adapter_spec.rb +419 -0
  152. data/spec/core/object_graph_spec.rb +86 -114
  153. data/spec/core/schema_generator_spec.rb +3 -3
  154. data/spec/core/schema_spec.rb +298 -38
  155. data/spec/core/spec_helper.rb +6 -48
  156. data/spec/extensions/association_autoreloading_spec.rb +1 -10
  157. data/spec/extensions/association_dependencies_spec.rb +2 -12
  158. data/spec/extensions/association_pks_spec.rb +66 -32
  159. data/spec/extensions/caching_spec.rb +23 -50
  160. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  161. data/spec/extensions/columns_introspection_spec.rb +91 -0
  162. data/spec/extensions/composition_spec.rb +18 -13
  163. data/spec/extensions/dataset_associations_spec.rb +199 -0
  164. data/spec/extensions/defaults_setter_spec.rb +64 -0
  165. data/spec/extensions/hook_class_methods_spec.rb +65 -91
  166. data/spec/extensions/identity_map_spec.rb +114 -22
  167. data/spec/extensions/instance_filters_spec.rb +11 -21
  168. data/spec/extensions/instance_hooks_spec.rb +53 -0
  169. data/spec/extensions/json_serializer_spec.rb +4 -5
  170. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  171. data/spec/extensions/list_spec.rb +17 -39
  172. data/spec/extensions/many_through_many_spec.rb +283 -250
  173. data/spec/extensions/migration_spec.rb +35 -32
  174. data/spec/extensions/named_timezones_spec.rb +23 -3
  175. data/spec/extensions/nested_attributes_spec.rb +97 -88
  176. data/spec/extensions/optimistic_locking_spec.rb +9 -20
  177. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  178. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  179. data/spec/extensions/prepared_statements_spec.rb +53 -0
  180. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  181. data/spec/extensions/pretty_table_spec.rb +1 -6
  182. data/spec/extensions/rcte_tree_spec.rb +41 -43
  183. data/spec/extensions/schema_dumper_spec.rb +5 -8
  184. data/spec/extensions/schema_spec.rb +13 -21
  185. data/spec/extensions/serialization_modification_detection_spec.rb +37 -0
  186. data/spec/extensions/serialization_spec.rb +72 -43
  187. data/spec/extensions/sharding_spec.rb +66 -140
  188. data/spec/extensions/single_table_inheritance_spec.rb +18 -29
  189. data/spec/extensions/spec_helper.rb +13 -61
  190. data/spec/extensions/sql_expr_spec.rb +20 -60
  191. data/spec/extensions/tactical_eager_loading_spec.rb +9 -19
  192. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  193. data/spec/extensions/timestamps_spec.rb +6 -6
  194. data/spec/extensions/to_dot_spec.rb +4 -7
  195. data/spec/extensions/touch_spec.rb +13 -14
  196. data/spec/extensions/tree_spec.rb +11 -26
  197. data/spec/extensions/typecast_on_load_spec.rb +1 -6
  198. data/spec/extensions/update_primary_key_spec.rb +30 -24
  199. data/spec/extensions/validation_class_methods_spec.rb +30 -51
  200. data/spec/extensions/validation_helpers_spec.rb +16 -35
  201. data/spec/extensions/xml_serializer_spec.rb +16 -4
  202. data/spec/integration/associations_test.rb +537 -21
  203. data/spec/integration/database_test.rb +4 -2
  204. data/spec/integration/dataset_test.rb +461 -65
  205. data/spec/integration/eager_loader_test.rb +19 -21
  206. data/spec/integration/model_test.rb +80 -1
  207. data/spec/integration/plugin_test.rb +304 -116
  208. data/spec/integration/prepared_statement_test.rb +212 -122
  209. data/spec/integration/schema_test.rb +161 -30
  210. data/spec/integration/spec_helper.rb +39 -30
  211. data/spec/integration/timezone_test.rb +38 -12
  212. data/spec/integration/transaction_test.rb +172 -5
  213. data/spec/integration/type_test.rb +17 -3
  214. data/spec/model/association_reflection_spec.rb +201 -7
  215. data/spec/model/associations_spec.rb +865 -662
  216. data/spec/model/base_spec.rb +275 -95
  217. data/spec/model/dataset_methods_spec.rb +7 -27
  218. data/spec/model/eager_loading_spec.rb +677 -676
  219. data/spec/model/hooks_spec.rb +309 -67
  220. data/spec/model/model_spec.rb +222 -168
  221. data/spec/model/plugins_spec.rb +24 -13
  222. data/spec/model/record_spec.rb +381 -218
  223. data/spec/model/spec_helper.rb +13 -71
  224. data/spec/model/validations_spec.rb +11 -0
  225. metadata +115 -73
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
 
@@ -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.
@@ -321,7 +338,7 @@ module Sequel
321
338
  # doesn't use the standard function calling convention, and it
322
339
  # doesn't work on all databases.
323
340
  def extract(datetime_part)
324
- Function.new(:extract, PlaceholderLiteralString.new("#{datetime_part} FROM ?", [self])).sql_number
341
+ NumericExpression.new(:extract, datetime_part, self)
325
342
  end
326
343
 
327
344
  # Return a BooleanExpression representation of +self+.
@@ -367,34 +384,12 @@ module Sequel
367
384
  # 'a'.lit <= :b # a <= "b"
368
385
  module InequalityMethods
369
386
  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
387
+ module_eval("def #{o}(o) BooleanExpression.new(#{o.inspect}, self, o) end", __FILE__, __LINE__)
378
388
  end
379
389
  end
380
390
 
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.
391
+ # Only exists for backwards compatibility, ignore it.
386
392
  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
393
  end
399
394
 
400
395
  # This module includes the standard mathematical methods (+, -, *, and /)
@@ -405,15 +400,26 @@ module Sequel
405
400
  # :a - :b # "a" - "b"
406
401
  # :a * :b # "a" * "b"
407
402
  # :a / :b # "a" / "b"
403
+ #
404
+ # One exception to this is if + is called with a +String+ or +StringExpression+,
405
+ # in which case the || operator is used instead of the + operator:
406
+ #
407
+ # :a + 'b' # "a" || 'b'
408
408
  module NumericMethods
409
409
  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)
416
- end
410
+ module_eval("def #{o}(o) NumericExpression.new(#{o.inspect}, self, o) end", __FILE__, __LINE__) unless o == :+
411
+ end
412
+
413
+ # Use || as the operator when called with StringExpression and String instances,
414
+ # and the + operator for LiteralStrings and all other types.
415
+ def +(ce)
416
+ case ce
417
+ when LiteralString
418
+ NumericExpression.new(:+, self, ce)
419
+ when StringExpression, String
420
+ StringExpression.new(:'||', self, ce)
421
+ else
422
+ NumericExpression.new(:+, self, ce)
417
423
  end
418
424
  end
419
425
  end
@@ -595,10 +601,27 @@ module Sequel
595
601
  end
596
602
  when StringExpression, NumericExpression
597
603
  raise(Sequel::Error, "cannot invert #{ce.inspect}")
604
+ when Constant
605
+ CONSTANT_INVERSIONS[ce] || raise(Sequel::Error, "cannot invert #{ce.inspect}")
598
606
  else
599
607
  BooleanExpression.new(:NOT, ce)
600
608
  end
601
609
  end
610
+
611
+ # Always use an AND operator for & on BooleanExpressions
612
+ def &(ce)
613
+ BooleanExpression.new(:AND, self, ce)
614
+ end
615
+
616
+ # Always use an OR operator for | on BooleanExpressions
617
+ def |(ce)
618
+ BooleanExpression.new(:OR, self, ce)
619
+ end
620
+
621
+ # Return self instead of creating a new object to save on memory.
622
+ def sql_boolean
623
+ self
624
+ end
602
625
  end
603
626
 
604
627
  # Represents an SQL CASE expression, used for conditional branching in SQL.
@@ -666,6 +689,21 @@ module Sequel
666
689
  include CastMethods
667
690
  include OrderMethods
668
691
  include SubscriptMethods
692
+
693
+ # Return a BooleanExpression with the same op and args.
694
+ def sql_boolean
695
+ BooleanExpression.new(self.op, *self.args)
696
+ end
697
+
698
+ # Return a NumericExpression with the same op and args.
699
+ def sql_number
700
+ NumericExpression.new(self.op, *self.args)
701
+ end
702
+
703
+ # Return a StringExpression with the same op and args.
704
+ def sql_string
705
+ StringExpression.new(self.op, *self.args)
706
+ end
669
707
  end
670
708
 
671
709
  # Represents constants or psuedo-constants (e.g. +CURRENT_DATE+) in SQL.
@@ -706,6 +744,12 @@ module Sequel
706
744
  NOTNULL = NegativeBooleanConstant.new(nil)
707
745
  end
708
746
 
747
+ class ComplexExpression
748
+ # A hash of the opposite for each constant, used for inverting constants.
749
+ CONSTANT_INVERSIONS = {Constants::TRUE=>Constants::FALSE, Constants::FALSE=>Constants::TRUE,
750
+ Constants::NULL=>Constants::NOTNULL, Constants::NOTNULL=>Constants::NULL}
751
+ end
752
+
709
753
  # Represents an SQL function call.
710
754
  class Function < GenericExpression
711
755
  # The array of arguments to pass to the function (may be blank)
@@ -834,7 +878,16 @@ module Sequel
834
878
  include BitwiseMethods
835
879
  include NumericMethods
836
880
  include InequalityMethods
837
- include NoBooleanInputMethods
881
+
882
+ # Always use + for + operator for NumericExpressions.
883
+ def +(ce)
884
+ NumericExpression.new(:+, self, ce)
885
+ end
886
+
887
+ # Return self instead of creating a new object to save on memory.
888
+ def sql_number
889
+ self
890
+ end
838
891
  end
839
892
 
840
893
  # Represents a column/expression to order the result set by.
@@ -900,7 +953,6 @@ module Sequel
900
953
  include StringMethods
901
954
  include StringConcatenationMethods
902
955
  include InequalityMethods
903
- include NoBooleanInputMethods
904
956
 
905
957
  # Map of [regexp, case_insenstive] to +ComplexExpression+ operator symbol
906
958
  LIKE_MAP = {[true, true]=>:'~*', [true, false]=>:~, [false, true]=>:ILIKE, [false, false]=>:LIKE}
@@ -949,6 +1001,11 @@ module Sequel
949
1001
  end
950
1002
  end
951
1003
  private_class_method :like_element
1004
+
1005
+ # Return self instead of creating a new object to save on memory.
1006
+ def sql_string
1007
+ self
1008
+ end
952
1009
  end
953
1010
 
954
1011
  # Represents an SQL array access, with multiple possible arguments.
@@ -39,6 +39,48 @@ module Sequel
39
39
  convert_output_timestamp(v, Sequel.database_timezone)
40
40
  end
41
41
 
42
+ # Converts the object to the given +output_timezone+.
43
+ def convert_output_timestamp(v, output_timezone)
44
+ if output_timezone
45
+ if v.is_a?(DateTime)
46
+ case output_timezone
47
+ when :utc
48
+ v.new_offset(0)
49
+ when :local
50
+ v.new_offset(local_offset_for_datetime(v))
51
+ else
52
+ convert_output_datetime_other(v, output_timezone)
53
+ end
54
+ else
55
+ v.send(output_timezone == :utc ? :getutc : :getlocal)
56
+ end
57
+ else
58
+ v
59
+ end
60
+ end
61
+
62
+ # Converts the given object from the given input timezone to the
63
+ # +application_timezone+ using +convert_input_timestamp+ and
64
+ # +convert_output_timestamp+.
65
+ def convert_timestamp(v, input_timezone)
66
+ begin
67
+ if v.is_a?(Date) && !v.is_a?(DateTime)
68
+ # Dates handled specially as they are assumed to already be in the application_timezone
69
+ if datetime_class == DateTime
70
+ DateTime.civil(v.year, v.month, v.day, 0, 0, 0, application_timezone == :local ? (defined?(Rational) ? Rational(Time.local(v.year, v.month, v.day).utc_offset, 86400) : Time.local(v.year, v.month, v.day).utc_offset/86400.0) : 0)
71
+ else
72
+ Time.send(application_timezone == :utc ? :utc : :local, v.year, v.month, v.day)
73
+ end
74
+ else
75
+ convert_output_timestamp(convert_input_timestamp(v, input_timezone), application_timezone)
76
+ end
77
+ rescue InvalidValue
78
+ raise
79
+ rescue => e
80
+ raise convert_exception_class(e, InvalidValue)
81
+ end
82
+ end
83
+
42
84
  # Convert the given object into an object of <tt>Sequel.datetime_class</tt> in the
43
85
  # +application_timezone+. Used when coverting datetime/timestamp columns
44
86
  # returned by the database.
@@ -103,12 +145,24 @@ module Sequel
103
145
  v2
104
146
  end
105
147
  when Array
106
- y, mo, d, h, mi, s = v
148
+ y, mo, d, h, mi, s, ns, off = v
107
149
  if datetime_class == DateTime
108
- convert_input_datetime_no_offset(DateTime.civil(y, mo, d, h, mi, s, 0), input_timezone)
150
+ s += (defined?(Rational) ? Rational(ns, 1000000000) : ns/1000000000.0) if ns
151
+ if off
152
+ DateTime.civil(y, mo, d, h, mi, s, off)
153
+ else
154
+ convert_input_datetime_no_offset(DateTime.civil(y, mo, d, h, mi, s), input_timezone)
155
+ end
109
156
  else
110
- Time.send(input_timezone == :utc ? :utc : :local, y, mo, d, h, mi, s)
157
+ Time.send(input_timezone == :utc ? :utc : :local, y, mo, d, h, mi, s, (ns ? ns / 1000.0 : 0))
158
+ end
159
+ when Hash
160
+ ary = [:year, :month, :day, :hour, :minute, :second, :nanos].map{|x| (v[x] || v[x.to_s]).to_i}
161
+ if (offset = (v[:offset] || v['offset']))
162
+ ary << offset
111
163
  end
164
+ convert_input_timestamp(ary, input_timezone)
165
+ convert_input_timestamp(ary, input_timezone)
112
166
  when Time
113
167
  if datetime_class == DateTime
114
168
  v.respond_to?(:to_datetime) ? v.to_datetime : string_to_datetime(v.iso8601)
@@ -121,8 +175,6 @@ module Sequel
121
175
  else
122
176
  v.respond_to?(:to_time) ? v.to_time : string_to_datetime(v.to_s)
123
177
  end
124
- when Date
125
- convert_input_timestamp(v.to_s, input_timezone)
126
178
  else
127
179
  raise InvalidValue, "Invalid convert_input_timestamp type: #{v.inspect}"
128
180
  end
@@ -135,39 +187,6 @@ module Sequel
135
187
  raise InvalidValue, "Invalid output_timezone: #{output_timezone.inspect}"
136
188
  end
137
189
 
138
- # Converts the object to the given +output_timezone+.
139
- def convert_output_timestamp(v, output_timezone)
140
- if output_timezone
141
- if v.is_a?(DateTime)
142
- case output_timezone
143
- when :utc
144
- v.new_offset(0)
145
- when :local
146
- v.new_offset(local_offset_for_datetime(v))
147
- else
148
- convert_output_datetime_other(v, output_timezone)
149
- end
150
- else
151
- v.send(output_timezone == :utc ? :getutc : :getlocal)
152
- end
153
- else
154
- v
155
- end
156
- end
157
-
158
- # Converts the given object from the given input timezone to the
159
- # +application_timezone+ using +convert_input_timestamp+ and
160
- # +convert_output_timestamp+.
161
- def convert_timestamp(v, input_timezone)
162
- begin
163
- convert_output_timestamp(convert_input_timestamp(v, input_timezone), Sequel.application_timezone)
164
- rescue InvalidValue
165
- raise
166
- rescue => e
167
- raise convert_exception_class(e, InvalidValue)
168
- end
169
- end
170
-
171
190
  # Convert the timezone setter argument. Returns argument given by default,
172
191
  # exists for easier overriding in extensions.
173
192
  def convert_timezone_setter_arg(tz)
@@ -3,7 +3,7 @@ module Sequel
3
3
  MAJOR = 3
4
4
  # The minor version of Sequel. Bumped for every non-patch level
5
5
  # release, generally around once a month.
6
- MINOR = 21
6
+ MINOR = 31
7
7
  # The tiny version of Sequel. Usually 0, only bumped for bugfix
8
8
  # releases that fix regressions from previous versions.
9
9
  TINY = 0
@@ -0,0 +1,146 @@
1
+ #!/usr/bin/env ruby
2
+ # coding: utf-8
3
+ #Author: Roy L Zuo (roylzuo at gmail dot com)
4
+ #Description:
5
+
6
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper.rb')
7
+
8
+ require ENV['SEQUEL_DB2_SPEC_REQUIRE'] if ENV['SEQUEL_DB2_SPEC_REQUIRE']
9
+
10
+ unless defined?(DB2_DB)
11
+ DB2_DB = Sequel.connect(ENV['SEQUEL_DB2_SPEC_DB']||DB2_URL)
12
+ end
13
+
14
+ if DB2_DB.table_exists?(:test)
15
+ DB2_DB.drop_table :test
16
+ end
17
+ INTEGRATION_DB = DB2_DB unless defined?(INTEGRATION_DB)
18
+
19
+ describe Sequel::Database do
20
+ before do
21
+ @db = DB2_DB
22
+ @db.create_table(:test){String :a}
23
+ @ds = @db[:test]
24
+ end
25
+
26
+ after do
27
+ @db.drop_table(:test)
28
+ end
29
+
30
+ specify "should provide disconnect functionality after preparing a connection" do
31
+ @ds.prepare(:first, :a).call
32
+ @db.disconnect
33
+ @db.pool.size.should == 0
34
+ end
35
+
36
+ specify "should return version correctly" do
37
+ @db.db2_version.should match(/DB2 v/i)
38
+ end
39
+ end
40
+
41
+ describe "Simple Dataset operations" do
42
+ before do
43
+ DB2_DB.create_table!(:items) do
44
+ Integer :id, :primary_key => true
45
+ Integer :number
46
+ end
47
+ @ds = DB2_DB[:items]
48
+ @ds.insert(:number=>10, :id => 1 )
49
+ end
50
+ after do
51
+ DB2_DB.drop_table(:items)
52
+ end
53
+ cspecify "should insert with a primary key specified", :mssql do
54
+ @ds.insert(:id=>100, :number=>20)
55
+ @ds.count.should == 2
56
+ @ds.order(:id).all.should == [{:id=>1, :number=>10}, {:id=>100, :number=>20}]
57
+ end
58
+ end
59
+
60
+ describe Sequel::Database do
61
+ before do
62
+ @db = DB2_DB
63
+ end
64
+ after do
65
+ @db.drop_table(:items)
66
+ end
67
+ specify "should parse primary keys from the schema properly" do
68
+ @db.create_table!(:items){Integer :number}
69
+ @db.schema(:items).collect{|k,v| k if v[:primary_key]}.compact.should == []
70
+ @db.create_table!(:items){primary_key :number}
71
+ @db.schema(:items).collect{|k,v| k if v[:primary_key]}.compact.should == [:number]
72
+ @db.create_table!(:items){Integer :number1, :null => false; Integer :number2, :null => false; primary_key [:number1, :number2]}
73
+ @db.schema(:items).collect{|k,v| k if v[:primary_key]}.compact.should == [:number1, :number2]
74
+ end
75
+ end
76
+
77
+ describe "Sequel::IBMDB.convert_smallint_to_bool" do
78
+ before do
79
+ @db = DB2_DB
80
+ @db.create_table!(:booltest){column :b, 'smallint'; column :i, 'integer'}
81
+ @ds = @db[:booltest]
82
+ end
83
+ after do
84
+ Sequel::IBMDB.convert_smallint_to_bool = true
85
+ @db.drop_table(:booltest)
86
+ end
87
+
88
+ specify "should consider smallint datatypes as boolean if set, but not larger smallints" do
89
+ @db.schema(:booltest, :reload=>true).first.last[:type].should == :boolean
90
+ @db.schema(:booltest, :reload=>true).first.last[:db_type].should match /smallint/i
91
+ Sequel::IBMDB.convert_smallint_to_bool = false
92
+ @db.schema(:booltest, :reload=>true).first.last[:type].should == :integer
93
+ @db.schema(:booltest, :reload=>true).first.last[:db_type].should match /smallint/i
94
+ end
95
+
96
+ specify "should return smallints as bools and integers as integers when set" do
97
+ Sequel::IBMDB.convert_smallint_to_bool = true
98
+ @ds.delete
99
+ @ds << {:b=>true, :i=>10}
100
+ @ds.all.should == [{:b=>true, :i=>10}]
101
+ @ds.delete
102
+ @ds << {:b=>false, :i=>0}
103
+ @ds.all.should == [{:b=>false, :i=>0}]
104
+ @ds.delete
105
+ @ds << {:b=>true, :i=>1}
106
+ @ds.all.should == [{:b=>true, :i=>1}]
107
+ end
108
+
109
+ specify "should return all smallints as integers when unset" do
110
+ Sequel::IBMDB.convert_smallint_to_bool = false
111
+ @ds.delete
112
+ @ds << {:b=>true, :i=>10}
113
+ @ds.all.should == [{:b=>1, :i=>10}]
114
+ @ds.delete
115
+ @ds << {:b=>false, :i=>0}
116
+ @ds.all.should == [{:b=>0, :i=>0}]
117
+
118
+ @ds.delete
119
+ @ds << {:b=>1, :i=>10}
120
+ @ds.all.should == [{:b=>1, :i=>10}]
121
+ @ds.delete
122
+ @ds << {:b=>0, :i=>0}
123
+ @ds.all.should == [{:b=>0, :i=>0}]
124
+ end
125
+ end if DB2_DB.adapter_scheme == :ibmdb
126
+
127
+ describe "Simple Dataset operations in transactions" do
128
+ before do
129
+ DB2_DB.create_table!(:items_insert_in_transaction) do
130
+ Integer :id, :primary_key => true
131
+ integer :number
132
+ end
133
+ @ds = DB2_DB[:items_insert_in_transaction]
134
+ end
135
+ after do
136
+ DB2_DB.drop_table(:items_insert_in_transaction)
137
+ end
138
+
139
+ specify "should insert correctly with a primary key specified inside a transaction" do
140
+ DB2_DB.transaction do
141
+ @ds.insert(:id=>100, :number=>20)
142
+ @ds.count.should == 1
143
+ @ds.order(:id).all.should == [{:id=>100, :number=>20}]
144
+ end
145
+ end
146
+ end