sequel 3.21.0 → 3.29.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 (200) hide show
  1. data/CHANGELOG +413 -3
  2. data/README.rdoc +20 -6
  3. data/Rakefile +23 -16
  4. data/bin/sequel +1 -5
  5. data/doc/association_basics.rdoc +210 -43
  6. data/doc/dataset_basics.rdoc +4 -4
  7. data/doc/mass_assignment.rdoc +54 -0
  8. data/doc/migration.rdoc +15 -538
  9. data/doc/model_hooks.rdoc +72 -27
  10. data/doc/opening_databases.rdoc +86 -50
  11. data/doc/prepared_statements.rdoc +40 -13
  12. data/doc/reflection.rdoc +8 -2
  13. data/doc/release_notes/3.22.0.txt +39 -0
  14. data/doc/release_notes/3.23.0.txt +172 -0
  15. data/doc/release_notes/3.24.0.txt +420 -0
  16. data/doc/release_notes/3.25.0.txt +88 -0
  17. data/doc/release_notes/3.26.0.txt +88 -0
  18. data/doc/release_notes/3.27.0.txt +82 -0
  19. data/doc/release_notes/3.28.0.txt +304 -0
  20. data/doc/release_notes/3.29.0.txt +459 -0
  21. data/doc/schema_modification.rdoc +547 -0
  22. data/doc/sharding.rdoc +7 -1
  23. data/doc/testing.rdoc +115 -0
  24. data/doc/transactions.rdoc +137 -0
  25. data/doc/virtual_rows.rdoc +2 -2
  26. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  27. data/lib/sequel/adapters/ado.rb +40 -18
  28. data/lib/sequel/adapters/amalgalite.rb +15 -7
  29. data/lib/sequel/adapters/db2.rb +175 -86
  30. data/lib/sequel/adapters/dbi.rb +15 -15
  31. data/lib/sequel/adapters/do/mysql.rb +0 -5
  32. data/lib/sequel/adapters/do/postgres.rb +0 -5
  33. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  34. data/lib/sequel/adapters/do.rb +17 -36
  35. data/lib/sequel/adapters/firebird.rb +27 -208
  36. data/lib/sequel/adapters/ibmdb.rb +448 -0
  37. data/lib/sequel/adapters/informix.rb +6 -23
  38. data/lib/sequel/adapters/jdbc/as400.rb +5 -31
  39. data/lib/sequel/adapters/jdbc/db2.rb +44 -0
  40. data/lib/sequel/adapters/jdbc/derby.rb +217 -0
  41. data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
  42. data/lib/sequel/adapters/jdbc/h2.rb +26 -17
  43. data/lib/sequel/adapters/jdbc/hsqldb.rb +166 -0
  44. data/lib/sequel/adapters/jdbc/informix.rb +26 -0
  45. data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
  46. data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
  47. data/lib/sequel/adapters/jdbc/mysql.rb +9 -10
  48. data/lib/sequel/adapters/jdbc/oracle.rb +67 -25
  49. data/lib/sequel/adapters/jdbc/postgresql.rb +6 -11
  50. data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
  51. data/lib/sequel/adapters/jdbc/sqlserver.rb +41 -0
  52. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  53. data/lib/sequel/adapters/jdbc.rb +107 -38
  54. data/lib/sequel/adapters/mock.rb +315 -0
  55. data/lib/sequel/adapters/mysql.rb +78 -182
  56. data/lib/sequel/adapters/mysql2.rb +24 -23
  57. data/lib/sequel/adapters/odbc/db2.rb +17 -0
  58. data/lib/sequel/adapters/odbc/mssql.rb +0 -5
  59. data/lib/sequel/adapters/odbc.rb +28 -9
  60. data/lib/sequel/adapters/openbase.rb +2 -4
  61. data/lib/sequel/adapters/oracle.rb +349 -51
  62. data/lib/sequel/adapters/postgres.rb +169 -21
  63. data/lib/sequel/adapters/shared/access.rb +21 -6
  64. data/lib/sequel/adapters/shared/db2.rb +288 -0
  65. data/lib/sequel/adapters/shared/firebird.rb +214 -0
  66. data/lib/sequel/adapters/shared/informix.rb +45 -0
  67. data/lib/sequel/adapters/shared/mssql.rb +108 -65
  68. data/lib/sequel/adapters/shared/mysql.rb +56 -13
  69. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
  70. data/lib/sequel/adapters/shared/oracle.rb +185 -34
  71. data/lib/sequel/adapters/shared/postgres.rb +107 -58
  72. data/lib/sequel/adapters/shared/progress.rb +0 -6
  73. data/lib/sequel/adapters/shared/sqlite.rb +168 -41
  74. data/lib/sequel/adapters/sqlite.rb +27 -18
  75. data/lib/sequel/adapters/swift/mysql.rb +9 -5
  76. data/lib/sequel/adapters/swift/postgres.rb +0 -5
  77. data/lib/sequel/adapters/swift/sqlite.rb +6 -4
  78. data/lib/sequel/adapters/swift.rb +5 -5
  79. data/lib/sequel/adapters/tinytds.rb +152 -17
  80. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +63 -0
  81. data/lib/sequel/ast_transformer.rb +190 -0
  82. data/lib/sequel/connection_pool/threaded.rb +3 -2
  83. data/lib/sequel/connection_pool.rb +1 -1
  84. data/lib/sequel/core.rb +46 -5
  85. data/lib/sequel/database/connecting.rb +5 -6
  86. data/lib/sequel/database/dataset.rb +4 -4
  87. data/lib/sequel/database/dataset_defaults.rb +59 -1
  88. data/lib/sequel/database/logging.rb +1 -1
  89. data/lib/sequel/database/misc.rb +100 -19
  90. data/lib/sequel/database/query.rb +153 -58
  91. data/lib/sequel/database/schema_generator.rb +8 -5
  92. data/lib/sequel/database/schema_methods.rb +59 -29
  93. data/lib/sequel/dataset/actions.rb +194 -57
  94. data/lib/sequel/dataset/features.rb +81 -8
  95. data/lib/sequel/dataset/graph.rb +8 -7
  96. data/lib/sequel/dataset/misc.rb +50 -23
  97. data/lib/sequel/dataset/mutation.rb +5 -6
  98. data/lib/sequel/dataset/prepared_statements.rb +32 -15
  99. data/lib/sequel/dataset/query.rb +223 -42
  100. data/lib/sequel/dataset/sql.rb +58 -61
  101. data/lib/sequel/dataset.rb +8 -0
  102. data/lib/sequel/exceptions.rb +4 -0
  103. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  104. data/lib/sequel/extensions/migration.rb +6 -4
  105. data/lib/sequel/extensions/named_timezones.rb +5 -0
  106. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  107. data/lib/sequel/extensions/to_dot.rb +95 -83
  108. data/lib/sequel/model/associations.rb +997 -331
  109. data/lib/sequel/model/base.rb +386 -129
  110. data/lib/sequel/model/errors.rb +1 -1
  111. data/lib/sequel/model/exceptions.rb +5 -1
  112. data/lib/sequel/model.rb +15 -8
  113. data/lib/sequel/plugins/association_pks.rb +22 -4
  114. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  115. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  116. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  117. data/lib/sequel/plugins/force_encoding.rb +6 -6
  118. data/lib/sequel/plugins/identity_map.rb +114 -7
  119. data/lib/sequel/plugins/many_through_many.rb +65 -7
  120. data/lib/sequel/plugins/prepared_statements.rb +151 -0
  121. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  122. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  123. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  124. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  125. data/lib/sequel/plugins/serialization.rb +6 -1
  126. data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
  127. data/lib/sequel/plugins/sharding.rb +12 -25
  128. data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
  129. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  130. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  131. data/lib/sequel/plugins/xml_serializer.rb +3 -3
  132. data/lib/sequel/sql.rb +107 -51
  133. data/lib/sequel/timezones.rb +44 -35
  134. data/lib/sequel/version.rb +1 -1
  135. data/spec/adapters/db2_spec.rb +146 -0
  136. data/spec/adapters/mssql_spec.rb +65 -29
  137. data/spec/adapters/mysql_spec.rb +122 -123
  138. data/spec/adapters/oracle_spec.rb +48 -76
  139. data/spec/adapters/postgres_spec.rb +213 -61
  140. data/spec/adapters/spec_helper.rb +6 -5
  141. data/spec/adapters/sqlite_spec.rb +35 -21
  142. data/spec/core/connection_pool_spec.rb +71 -92
  143. data/spec/core/core_sql_spec.rb +20 -31
  144. data/spec/core/database_spec.rb +729 -508
  145. data/spec/core/dataset_spec.rb +980 -1044
  146. data/spec/core/expression_filters_spec.rb +159 -42
  147. data/spec/core/mock_adapter_spec.rb +378 -0
  148. data/spec/core/object_graph_spec.rb +48 -114
  149. data/spec/core/schema_generator_spec.rb +3 -3
  150. data/spec/core/schema_spec.rb +298 -38
  151. data/spec/core/spec_helper.rb +6 -48
  152. data/spec/extensions/association_pks_spec.rb +38 -0
  153. data/spec/extensions/class_table_inheritance_spec.rb +1 -1
  154. data/spec/extensions/columns_introspection_spec.rb +91 -0
  155. data/spec/extensions/dataset_associations_spec.rb +199 -0
  156. data/spec/extensions/defaults_setter_spec.rb +64 -0
  157. data/spec/extensions/identity_map_spec.rb +162 -0
  158. data/spec/extensions/instance_hooks_spec.rb +71 -0
  159. data/spec/extensions/many_through_many_spec.rb +195 -20
  160. data/spec/extensions/migration_spec.rb +17 -17
  161. data/spec/extensions/named_timezones_spec.rb +22 -2
  162. data/spec/extensions/nested_attributes_spec.rb +4 -0
  163. data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
  164. data/spec/extensions/prepared_statements_safe_spec.rb +79 -0
  165. data/spec/extensions/prepared_statements_spec.rb +72 -0
  166. data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
  167. data/spec/extensions/schema_dumper_spec.rb +2 -2
  168. data/spec/extensions/schema_spec.rb +13 -21
  169. data/spec/extensions/serialization_modification_detection_spec.rb +37 -0
  170. data/spec/extensions/serialization_spec.rb +5 -8
  171. data/spec/extensions/single_table_inheritance_spec.rb +11 -0
  172. data/spec/extensions/spec_helper.rb +7 -1
  173. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  174. data/spec/extensions/to_dot_spec.rb +3 -5
  175. data/spec/extensions/typecast_on_load_spec.rb +1 -6
  176. data/spec/extensions/xml_serializer_spec.rb +16 -4
  177. data/spec/integration/associations_test.rb +522 -21
  178. data/spec/integration/database_test.rb +4 -2
  179. data/spec/integration/dataset_test.rb +375 -62
  180. data/spec/integration/eager_loader_test.rb +19 -21
  181. data/spec/integration/model_test.rb +80 -1
  182. data/spec/integration/plugin_test.rb +304 -116
  183. data/spec/integration/prepared_statement_test.rb +200 -120
  184. data/spec/integration/schema_test.rb +161 -30
  185. data/spec/integration/spec_helper.rb +39 -30
  186. data/spec/integration/timezone_test.rb +38 -12
  187. data/spec/integration/transaction_test.rb +172 -5
  188. data/spec/integration/type_test.rb +17 -3
  189. data/spec/model/association_reflection_spec.rb +174 -7
  190. data/spec/model/associations_spec.rb +849 -661
  191. data/spec/model/base_spec.rb +255 -95
  192. data/spec/model/dataset_methods_spec.rb +7 -27
  193. data/spec/model/eager_loading_spec.rb +640 -676
  194. data/spec/model/hooks_spec.rb +309 -67
  195. data/spec/model/model_spec.rb +207 -167
  196. data/spec/model/plugins_spec.rb +24 -13
  197. data/spec/model/record_spec.rb +321 -218
  198. data/spec/model/spec_helper.rb +13 -71
  199. data/spec/model/validations_spec.rb +11 -0
  200. metadata +95 -38
@@ -1,6 +1,6 @@
1
1
  module Sequel
2
2
  module Plugins
3
- # The update_primary_key plugin allows you to modify an objects
3
+ # The update_primary_key plugin allows you to modify an object's
4
4
  # primary key and then save the record. Sequel does not work
5
5
  # correctly with primary key modifications by default. Sequel
6
6
  # is designed to work with surrogate primary keys that never need to be
@@ -22,7 +22,7 @@ module Sequel
22
22
  module UpdatePrimaryKey
23
23
  module ClassMethods
24
24
  # Cache the pk_hash when loading records
25
- def load(h)
25
+ def call(h)
26
26
  r = super(h)
27
27
  r.pk_hash
28
28
  r
@@ -205,9 +205,9 @@ module Sequel
205
205
  klass.from_xml_node(node)
206
206
  end
207
207
  elsif cols.include?(k)
208
- self[k.to_sym] = node[:nil] ? nil : node.children.first.to_s
208
+ self[k.to_sym] = node.key?('nil') ? nil : node.children.first.to_s
209
209
  elsif meths.include?("#{k}=")
210
- send("#{k}=", node[:nil] ? nil : node.children.first.to_s)
210
+ send("#{k}=", node.key?('nil') ? nil : node.children.first.to_s)
211
211
  else
212
212
  raise Error, "Entry in XML not an association or column and no setter method exists: #{k}"
213
213
  end
@@ -266,7 +266,7 @@ module Sequel
266
266
 
267
267
  name_proc = model.xml_serialize_name_proc(opts)
268
268
  x = model.xml_builder(opts)
269
- x.send(name_proc[opts.fetch(:root_name, model.send(:underscore, model.name)).to_s]) do |x1|
269
+ x.send(name_proc[opts.fetch(:root_name, model.send(:underscore, model.name).gsub('/', '__')).to_s]) do |x1|
270
270
  cols.each do |c|
271
271
  attrs = {}
272
272
  if types
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.
@@ -144,14 +154,17 @@ module Sequel
144
154
  # Operator symbols that take exactly two arguments
145
155
  TWO_ARITY_OPERATORS = [:'=', :'!=', :LIKE, :'NOT LIKE', \
146
156
  :~, :'!~', :'~*', :'!~*', :ILIKE, :'NOT ILIKE'] + \
147
- INEQUALITY_OPERATORS + BITWISE_OPERATORS + IS_OPERATORS + IN_OPERATORS
157
+ INEQUALITY_OPERATORS + IS_OPERATORS + IN_OPERATORS
148
158
 
149
159
  # Operator symbols that take one or more arguments
150
- N_ARITY_OPERATORS = [:AND, :OR, :'||'] + MATHEMATICAL_OPERATORS
160
+ N_ARITY_OPERATORS = [:AND, :OR, :'||'] + MATHEMATICAL_OPERATORS + BITWISE_OPERATORS
151
161
 
152
162
  # Operator symbols that take only a single argument
153
163
  ONE_ARITY_OPERATORS = [:NOT, :NOOP, :'B~']
154
164
 
165
+ # Custom expressions that may have different syntax on different databases
166
+ CUSTOM_EXPRESSIONS = [:extract]
167
+
155
168
  # An array of args for this object
156
169
  attr_reader :args
157
170
 
@@ -180,6 +193,8 @@ module Sequel
180
193
  args[1] = orig_args[1] if IN_OPERATORS.include?(op)
181
194
  when *ONE_ARITY_OPERATORS
182
195
  raise(Error, "The #{op} operator requires a single argument") unless args.length == 1
196
+ when *CUSTOM_EXPRESSIONS
197
+ # nothing
183
198
  else
184
199
  raise(Error, "Invalid operator #{op}")
185
200
  end
@@ -219,14 +234,7 @@ module Sequel
219
234
  # ~:a.sql_number # ~"a"
220
235
  module BitwiseMethods
221
236
  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
237
+ module_eval("def #{o}(o) NumericExpression.new(#{o.inspect}, self, o) end", __FILE__, __LINE__)
230
238
  end
231
239
 
232
240
  # Do the bitwise compliment of the self
@@ -244,16 +252,24 @@ module Sequel
244
252
  # :a & :b # "a" AND "b"
245
253
  # :a | :b # "a" OR "b"
246
254
  # ~:a # NOT "a"
255
+ #
256
+ # One exception to this is when a NumericExpression or Integer is the argument
257
+ # to & or |, in which case a bitwise method will be used:
258
+ #
259
+ # :a & 1 # "a" & 1
260
+ # :a | (:b + 1) # "a" | ("b" + 1)
247
261
  module BooleanMethods
248
262
  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)
263
+ module_eval(<<-END, __FILE__, __LINE__+1)
264
+ def #{m}(o)
265
+ case o
266
+ when NumericExpression, Integer
267
+ NumericExpression.new(#{m.inspect}, self, o)
268
+ else
269
+ BooleanExpression.new(#{o.inspect}, self, o)
270
+ end
255
271
  end
256
- end
272
+ END
257
273
  end
258
274
 
259
275
  # Create a new BooleanExpression with NOT, representing the inversion of whatever self represents.
@@ -321,7 +337,7 @@ module Sequel
321
337
  # doesn't use the standard function calling convention, and it
322
338
  # doesn't work on all databases.
323
339
  def extract(datetime_part)
324
- Function.new(:extract, PlaceholderLiteralString.new("#{datetime_part} FROM ?", [self])).sql_number
340
+ NumericExpression.new(:extract, datetime_part, self)
325
341
  end
326
342
 
327
343
  # Return a BooleanExpression representation of +self+.
@@ -367,34 +383,12 @@ module Sequel
367
383
  # 'a'.lit <= :b # a <= "b"
368
384
  module InequalityMethods
369
385
  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
386
+ module_eval("def #{o}(o) BooleanExpression.new(#{o.inspect}, self, o) end", __FILE__, __LINE__)
378
387
  end
379
388
  end
380
389
 
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.
390
+ # Only exists for backwards compatibility, ignore it.
386
391
  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
392
  end
399
393
 
400
394
  # This module includes the standard mathematical methods (+, -, *, and /)
@@ -405,15 +399,26 @@ module Sequel
405
399
  # :a - :b # "a" - "b"
406
400
  # :a * :b # "a" * "b"
407
401
  # :a / :b # "a" / "b"
402
+ #
403
+ # One exception to this is if + is called with a +String+ or +StringExpression+,
404
+ # in which case the || operator is used instead of the + operator:
405
+ #
406
+ # :a + 'b' # "a" || 'b'
408
407
  module NumericMethods
409
408
  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
409
+ module_eval("def #{o}(o) NumericExpression.new(#{o.inspect}, self, o) end", __FILE__, __LINE__) unless o == :+
410
+ end
411
+
412
+ # Use || as the operator when called with StringExpression and String instances,
413
+ # and the + operator for LiteralStrings and all other types.
414
+ def +(ce)
415
+ case ce
416
+ when LiteralString
417
+ NumericExpression.new(:+, self, ce)
418
+ when StringExpression, String
419
+ StringExpression.new(:'||', self, ce)
420
+ else
421
+ NumericExpression.new(:+, self, ce)
417
422
  end
418
423
  end
419
424
  end
@@ -595,10 +600,27 @@ module Sequel
595
600
  end
596
601
  when StringExpression, NumericExpression
597
602
  raise(Sequel::Error, "cannot invert #{ce.inspect}")
603
+ when Constant
604
+ CONSTANT_INVERSIONS[ce] || raise(Sequel::Error, "cannot invert #{ce.inspect}")
598
605
  else
599
606
  BooleanExpression.new(:NOT, ce)
600
607
  end
601
608
  end
609
+
610
+ # Always use an AND operator for & on BooleanExpressions
611
+ def &(ce)
612
+ BooleanExpression.new(:AND, self, ce)
613
+ end
614
+
615
+ # Always use an OR operator for | on BooleanExpressions
616
+ def |(ce)
617
+ BooleanExpression.new(:OR, self, ce)
618
+ end
619
+
620
+ # Return self instead of creating a new object to save on memory.
621
+ def sql_boolean
622
+ self
623
+ end
602
624
  end
603
625
 
604
626
  # Represents an SQL CASE expression, used for conditional branching in SQL.
@@ -666,6 +688,21 @@ module Sequel
666
688
  include CastMethods
667
689
  include OrderMethods
668
690
  include SubscriptMethods
691
+
692
+ # Return a BooleanExpression with the same op and args.
693
+ def sql_boolean
694
+ BooleanExpression.new(self.op, *self.args)
695
+ end
696
+
697
+ # Return a NumericExpression with the same op and args.
698
+ def sql_number
699
+ NumericExpression.new(self.op, *self.args)
700
+ end
701
+
702
+ # Return a StringExpression with the same op and args.
703
+ def sql_string
704
+ StringExpression.new(self.op, *self.args)
705
+ end
669
706
  end
670
707
 
671
708
  # Represents constants or psuedo-constants (e.g. +CURRENT_DATE+) in SQL.
@@ -706,6 +743,12 @@ module Sequel
706
743
  NOTNULL = NegativeBooleanConstant.new(nil)
707
744
  end
708
745
 
746
+ class ComplexExpression
747
+ # A hash of the opposite for each constant, used for inverting constants.
748
+ CONSTANT_INVERSIONS = {Constants::TRUE=>Constants::FALSE, Constants::FALSE=>Constants::TRUE,
749
+ Constants::NULL=>Constants::NOTNULL, Constants::NOTNULL=>Constants::NULL}
750
+ end
751
+
709
752
  # Represents an SQL function call.
710
753
  class Function < GenericExpression
711
754
  # The array of arguments to pass to the function (may be blank)
@@ -834,7 +877,16 @@ module Sequel
834
877
  include BitwiseMethods
835
878
  include NumericMethods
836
879
  include InequalityMethods
837
- include NoBooleanInputMethods
880
+
881
+ # Always use + for + operator for NumericExpressions.
882
+ def +(ce)
883
+ NumericExpression.new(:+, self, ce)
884
+ end
885
+
886
+ # Return self instead of creating a new object to save on memory.
887
+ def sql_number
888
+ self
889
+ end
838
890
  end
839
891
 
840
892
  # Represents a column/expression to order the result set by.
@@ -900,7 +952,6 @@ module Sequel
900
952
  include StringMethods
901
953
  include StringConcatenationMethods
902
954
  include InequalityMethods
903
- include NoBooleanInputMethods
904
955
 
905
956
  # Map of [regexp, case_insenstive] to +ComplexExpression+ operator symbol
906
957
  LIKE_MAP = {[true, true]=>:'~*', [true, false]=>:~, [false, true]=>:ILIKE, [false, false]=>:LIKE}
@@ -949,6 +1000,11 @@ module Sequel
949
1000
  end
950
1001
  end
951
1002
  private_class_method :like_element
1003
+
1004
+ # Return self instead of creating a new object to save on memory.
1005
+ def sql_string
1006
+ self
1007
+ end
952
1008
  end
953
1009
 
954
1010
  # 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.
@@ -109,6 +151,8 @@ module Sequel
109
151
  else
110
152
  Time.send(input_timezone == :utc ? :utc : :local, y, mo, d, h, mi, s)
111
153
  end
154
+ when Hash
155
+ convert_input_timestamp([:year, :month, :day, :hour, :minute, :second].map{|x| (v[x] || v[x.to_s]).to_i}, input_timezone)
112
156
  when Time
113
157
  if datetime_class == DateTime
114
158
  v.respond_to?(:to_datetime) ? v.to_datetime : string_to_datetime(v.iso8601)
@@ -121,8 +165,6 @@ module Sequel
121
165
  else
122
166
  v.respond_to?(:to_time) ? v.to_time : string_to_datetime(v.to_s)
123
167
  end
124
- when Date
125
- convert_input_timestamp(v.to_s, input_timezone)
126
168
  else
127
169
  raise InvalidValue, "Invalid convert_input_timestamp type: #{v.inspect}"
128
170
  end
@@ -135,39 +177,6 @@ module Sequel
135
177
  raise InvalidValue, "Invalid output_timezone: #{output_timezone.inspect}"
136
178
  end
137
179
 
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
180
  # Convert the timezone setter argument. Returns argument given by default,
172
181
  # exists for easier overriding in extensions.
173
182
  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 = 29
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