sequel 4.48.0 → 4.49.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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +56 -0
  3. data/doc/advanced_associations.rdoc +1 -1
  4. data/doc/opening_databases.rdoc +3 -2
  5. data/doc/release_notes/4.49.0.txt +222 -0
  6. data/lib/sequel/adapters/ibmdb.rb +6 -1
  7. data/lib/sequel/adapters/jdbc.rb +3 -1
  8. data/lib/sequel/adapters/jdbc/h2.rb +10 -1
  9. data/lib/sequel/adapters/jdbc/postgresql.rb +3 -2
  10. data/lib/sequel/adapters/jdbc/sqlserver.rb +9 -2
  11. data/lib/sequel/adapters/mock.rb +3 -0
  12. data/lib/sequel/adapters/mysql2.rb +1 -1
  13. data/lib/sequel/adapters/postgres.rb +2 -1
  14. data/lib/sequel/adapters/shared/mysql.rb +4 -1
  15. data/lib/sequel/adapters/shared/oracle.rb +26 -3
  16. data/lib/sequel/connection_pool.rb +9 -2
  17. data/lib/sequel/connection_pool/sharded_single.rb +1 -1
  18. data/lib/sequel/connection_pool/sharded_threaded.rb +1 -1
  19. data/lib/sequel/connection_pool/single.rb +2 -2
  20. data/lib/sequel/connection_pool/threaded.rb +2 -2
  21. data/lib/sequel/database/connecting.rb +3 -3
  22. data/lib/sequel/database/dataset_defaults.rb +14 -1
  23. data/lib/sequel/dataset.rb +1 -1
  24. data/lib/sequel/dataset/actions.rb +54 -0
  25. data/lib/sequel/dataset/dataset_module.rb +58 -0
  26. data/lib/sequel/dataset/query.rb +3 -3
  27. data/lib/sequel/exceptions.rb +8 -0
  28. data/lib/sequel/extensions/_model_pg_row.rb +5 -2
  29. data/lib/sequel/extensions/current_datetime_timestamp.rb +2 -1
  30. data/lib/sequel/extensions/date_arithmetic.rb +1 -0
  31. data/lib/sequel/extensions/duplicate_columns_handler.rb +2 -2
  32. data/lib/sequel/extensions/migration.rb +5 -2
  33. data/lib/sequel/extensions/null_dataset.rb +1 -0
  34. data/lib/sequel/model/associations.rb +3 -0
  35. data/lib/sequel/model/base.rb +10 -55
  36. data/lib/sequel/model/dataset_module.rb +5 -43
  37. data/lib/sequel/model/errors.rb +2 -1
  38. data/lib/sequel/model/inflections.rb +17 -5
  39. data/lib/sequel/plugins/active_model.rb +2 -2
  40. data/lib/sequel/plugins/class_table_inheritance.rb +1 -0
  41. data/lib/sequel/plugins/composition.rb +2 -2
  42. data/lib/sequel/plugins/dataset_associations.rb +25 -13
  43. data/lib/sequel/plugins/json_serializer.rb +2 -2
  44. data/lib/sequel/plugins/pg_row.rb +4 -2
  45. data/lib/sequel/plugins/serialization.rb +1 -0
  46. data/lib/sequel/plugins/single_table_inheritance.rb +6 -1
  47. data/lib/sequel/plugins/touch.rb +2 -1
  48. data/lib/sequel/plugins/validation_helpers.rb +10 -2
  49. data/lib/sequel/sql.rb +16 -7
  50. data/lib/sequel/version.rb +1 -1
  51. data/spec/adapters/mssql_spec.rb +4 -4
  52. data/spec/adapters/mysql_spec.rb +5 -1
  53. data/spec/adapters/oracle_spec.rb +4 -0
  54. data/spec/bin_spec.rb +7 -1
  55. data/spec/core/connection_pool_spec.rb +28 -14
  56. data/spec/core/database_spec.rb +149 -0
  57. data/spec/core/dataset_spec.rb +173 -0
  58. data/spec/extensions/class_table_inheritance_spec.rb +58 -17
  59. data/spec/extensions/composition_spec.rb +13 -0
  60. data/spec/extensions/dataset_associations_spec.rb +12 -0
  61. data/spec/extensions/many_through_many_spec.rb +4 -4
  62. data/spec/extensions/null_dataset_spec.rb +1 -1
  63. data/spec/extensions/serialization_spec.rb +1 -1
  64. data/spec/extensions/single_table_inheritance_spec.rb +16 -0
  65. data/spec/extensions/validation_helpers_spec.rb +1 -2
  66. data/spec/integration/associations_test.rb +8 -0
  67. data/spec/integration/plugin_test.rb +8 -3
  68. data/spec/model/association_reflection_spec.rb +1 -1
  69. data/spec/model/associations_spec.rb +29 -9
  70. data/spec/model/class_dataset_methods_spec.rb +6 -0
  71. data/spec/model/eager_loading_spec.rb +8 -8
  72. data/spec/model/plugins_spec.rb +34 -0
  73. data/spec/model/record_spec.rb +1 -1
  74. data/spec/spec_config.rb +2 -0
  75. metadata +5 -2
@@ -53,7 +53,9 @@ module Sequel
53
53
  # Address.load(:street=>'123 Foo St', :city=>'Bar Town', :zip=>'12345'))
54
54
  module PgRow
55
55
  ROW = 'ROW'.freeze
56
+ Sequel::Deprecation.deprecate_constant(self, :ROW)
56
57
  CAST = '::'.freeze
58
+ Sequel::Deprecation.deprecate_constant(self, :CAST)
57
59
 
58
60
  # When loading the extension, make sure the database has the pg_row extension
59
61
  # loaded, load the custom database extensions, and automatically register the
@@ -75,9 +77,9 @@ module Sequel
75
77
  module InstanceMethods
76
78
  # Literalize the model instance and append it to the sql.
77
79
  def sql_literal_append(ds, sql)
78
- sql << ROW
80
+ sql << 'ROW'
79
81
  ds.literal_append(sql, values.values_at(*columns))
80
- sql << CAST
82
+ sql << '::'
81
83
  ds.quote_schema_table_append(sql, model.dataset.first_source_table)
82
84
  end
83
85
  end
@@ -149,6 +149,7 @@ module Sequel
149
149
  # The columns that will be serialized. This is only for
150
150
  # backwards compatibility, use serialization_map in new code.
151
151
  def serialized_columns
152
+ Sequel::Deprecation.deprecate("#{self}.serialized_columns in the serialization plugin", "Use #{self}.serialization_map.keys instead")
152
153
  serialization_map.keys
153
154
  end
154
155
 
@@ -182,7 +182,12 @@ module Sequel
182
182
  # Return an instance of the class specified by sti_key,
183
183
  # used by the row_proc.
184
184
  def sti_load(r)
185
- sti_class(sti_model_map[r[sti_key]]).call(r)
185
+ sti_class_from_sti_key(r[sti_key]).call(r)
186
+ end
187
+
188
+ # Return the sti class based on one of the keys from sti_model_map.
189
+ def sti_class_from_sti_key(key)
190
+ sti_class(sti_model_map[key])
186
191
  end
187
192
 
188
193
  # Make sure that all subclasses of the parent class correctly include
@@ -31,6 +31,7 @@ module Sequel
31
31
  module Touch
32
32
  # The default column to update when touching
33
33
  TOUCH_COLUMN_DEFAULT = :updated_at
34
+ Sequel::Deprecation.deprecate_constant(self, :TOUCH_COLUMN_DEFAULT)
34
35
 
35
36
  def self.apply(model, opts=OPTS)
36
37
  model.instance_variable_set(:@touched_associations, {})
@@ -46,7 +47,7 @@ module Sequel
46
47
  # If a hash is used, the value is used as the column to update.
47
48
  # :column :: The column to modify when touching a model instance.
48
49
  def self.configure(model, opts=OPTS)
49
- model.touch_column = opts[:column] || TOUCH_COLUMN_DEFAULT if opts[:column] || !model.touch_column
50
+ model.touch_column = opts[:column] || :updated_at if opts[:column] || !model.touch_column
50
51
  model.touch_associations(opts[:associations]) if opts[:associations]
51
52
  end
52
53
 
@@ -90,6 +90,8 @@ module Sequel
90
90
  :presence=>{:message=>lambda{"is not present"}},
91
91
  :unique=>{:message=>lambda{'is already taken'}}
92
92
  }
93
+ DEFAULT__OPTIONS = DEFAULT_OPTIONS
94
+ Sequel::Deprecation.deprecate_constant(self, :DEFAULT_OPTIONS)
93
95
 
94
96
  module InstanceMethods
95
97
  # Check that the attribute values are the given exact length.
@@ -129,7 +131,13 @@ module Sequel
129
131
  # Accepts a :nil_message option that is the error message to use when the
130
132
  # value is nil instead of being too long.
131
133
  def validates_max_length(max, atts, opts=OPTS)
132
- validatable_attributes_for_type(:max_length, atts, opts){|a,v,m| v ? validation_error_message(m, max) : validation_error_message(opts[:nil_message] || DEFAULT_OPTIONS[:max_length][:nil_message]) if v.nil? || v.length > max}
134
+ validatable_attributes_for_type(:max_length, atts, opts) do |a,v,m|
135
+ if v.nil?
136
+ validation_error_message(opts[:nil_message] || default_validation_helpers_options(:max_length)[:nil_message])
137
+ elsif v.length > max
138
+ validation_error_message(m, max)
139
+ end
140
+ end
133
141
  end
134
142
 
135
143
  # Check that the attribute values are not shorter than the given min length.
@@ -268,7 +276,7 @@ module Sequel
268
276
  # The hash return must include a :message option that is either a
269
277
  # proc or string.
270
278
  def default_validation_helpers_options(type)
271
- DEFAULT_OPTIONS[type]
279
+ DEFAULT__OPTIONS[type]
272
280
  end
273
281
 
274
282
  # Skip validating any attribute that matches one of the allow_* options.
@@ -1030,10 +1030,13 @@ module Sequel
1030
1030
  # The expression to alias
1031
1031
  attr_reader :expression
1032
1032
 
1033
- # The alias to use for the expression, not +alias+ since that is
1034
- # a keyword in ruby.
1035
- attr_reader :aliaz
1036
- alias_method :alias, :aliaz
1033
+ # The alias to use for the expression
1034
+ attr_reader :alias
1035
+
1036
+ def aliaz
1037
+ Sequel::Deprecation.deprecate("Sequel::SQL::AliasedExpression#aliaz", "Use #alias instead")
1038
+ self.alias
1039
+ end
1037
1040
 
1038
1041
  # The columns aliases to use, for when the aliased expression is
1039
1042
  # a record or set of records (such as a dataset).
@@ -1042,7 +1045,7 @@ module Sequel
1042
1045
  # Create an object with the given expression and alias.
1043
1046
  def initialize(expression, aliaz, columns=nil)
1044
1047
  @expression = expression
1045
- @aliaz = aliaz
1048
+ @alias = aliaz
1046
1049
  @columns = columns
1047
1050
  freeze
1048
1051
  end
@@ -1359,7 +1362,11 @@ module Sequel
1359
1362
 
1360
1363
  # The SQL function to call
1361
1364
  attr_reader :name
1362
- alias f name
1365
+
1366
+ def f
1367
+ Sequel::Deprecation.deprecate("Sequel::SQL::Function#f", "Use #name instead")
1368
+ name
1369
+ end
1363
1370
 
1364
1371
  # The array of arguments to pass to the function (may be blank)
1365
1372
  attr_reader :args
@@ -1917,7 +1924,9 @@ module Sequel
1917
1924
  # For a more detailed explanation, see the {Virtual Rows guide}[rdoc-ref:doc/virtual_rows.rdoc].
1918
1925
  class VirtualRow < BasicObject
1919
1926
  QUESTION_MARK = LiteralString.new('?').freeze
1927
+ Sequel::Deprecation.deprecate_constant(self, :QUESTION_MARK)
1920
1928
  DOUBLE_UNDERSCORE = '__'.freeze
1929
+ Sequel::Deprecation.deprecate_constant(self, :DOUBLE_UNDERSCORE)
1921
1930
 
1922
1931
  include OperatorBuilders
1923
1932
 
@@ -1972,7 +1981,7 @@ module Sequel
1972
1981
  end
1973
1982
  elsif args.empty?
1974
1983
  if split = Sequel.split_symbols?
1975
- table, column = m.to_s.split(DOUBLE_UNDERSCORE, 2)
1984
+ table, column = m.to_s.split('__', 2)
1976
1985
  if column && split == :deprecated
1977
1986
  Sequel::Deprecation.deprecate("Splitting virtual row method names", "Either set Sequel.split_symbols = true, or change #{m.inspect} to #{table}[:#{column}]")
1978
1987
  end
@@ -5,7 +5,7 @@ module Sequel
5
5
  MAJOR = 4
6
6
  # The minor version of Sequel. Bumped for every non-patch level
7
7
  # release, generally around once a month.
8
- MINOR = 48
8
+ MINOR = 49
9
9
  # The tiny version of Sequel. Usually 0, only bumped for bugfix
10
10
  # releases that fix regressions from previous versions.
11
11
  TINY = 0
@@ -17,13 +17,13 @@ describe "A MSSQL database" do
17
17
  end
18
18
 
19
19
  it "should be able to read fractional part of timestamp" do
20
- rs = @db["select getutcdate() as full_date, cast(datepart(millisecond, getutcdate()) as int) as milliseconds"].first
21
- rs[:milliseconds].must_equal rs[:full_date].usec/1000
20
+ rs = @db["select getutcdate() as full_date, cast(round(datepart(millisecond, getutcdate()), 0) as int) as milliseconds"].first
21
+ rs[:milliseconds].must_be_close_to(rs[:full_date].usec/1000, 2)
22
22
  end
23
23
 
24
24
  it "should be able to write fractional part of timestamp" do
25
- t = Time.utc(2001, 12, 31, 23, 59, 59, 997000)
26
- (t.usec/1000).must_equal @db["select cast(datepart(millisecond, ?) as int) as milliseconds", t].get
25
+ t = Time.utc(2001, 12, 31, 23, 59, 59, 996000)
26
+ (t.usec/1000).must_equal @db["select cast(round(datepart(millisecond, ?), 0) as int) as milliseconds", t].get
27
27
  end
28
28
 
29
29
  it "should not raise an error when getting the server version" do
@@ -318,7 +318,7 @@ describe "MySQL join expressions" do
318
318
  it "should support cross joins" do
319
319
  @ds.join_table(:cross, :nodes).sql.must_equal 'SELECT * FROM `nodes` CROSS JOIN `nodes`'
320
320
  end
321
- it "should support cross joins as inner joins if conditions are used" do
321
+ deprecated "should support cross joins as inner joins if conditions are used" do
322
322
  @ds.join_table(:cross, :nodes, :id=>:id).sql.must_equal 'SELECT * FROM `nodes` INNER JOIN `nodes` ON (`nodes`.`id` = `nodes`.`id`)'
323
323
  end
324
324
  it "should support straight joins (force left table to be read before right)" do
@@ -1367,6 +1367,10 @@ if DB.adapter_scheme == :mysql2
1367
1367
  it "should correctly handle early returning when streaming results" do
1368
1368
  3.times{@ds.each{|r| break r[:a]}.must_equal 0}
1369
1369
  end
1370
+
1371
+ it "#paged_each should bypass streaming when :stream => false passed in" do
1372
+ DB[:a].order(:a).paged_each(:stream => false){|x| DB[:a].first; break}
1373
+ end
1370
1374
  end
1371
1375
  end
1372
1376
 
@@ -2,6 +2,10 @@ SEQUEL_ADAPTER_TEST = :oracle
2
2
 
3
3
  require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper.rb')
4
4
 
5
+ unless DB.opts[:autosequence]
6
+ warn "Running oracle adapter specs without :autosequence Database option results in many errors, use the :autosequence Database option when testing"
7
+ end
8
+
5
9
  describe "An Oracle database" do
6
10
  before(:all) do
7
11
  DB.create_table!(:items) do
@@ -40,7 +40,13 @@ describe "bin/sequel" do
40
40
  DB2.disconnect
41
41
  File.delete(BIN_SPEC_DB) if File.file?(BIN_SPEC_DB)
42
42
  File.delete(BIN_SPEC_DB2) if File.file?(BIN_SPEC_DB2)
43
- File.delete(TMP_FILE) if File.file?(TMP_FILE)
43
+ if File.file?(TMP_FILE)
44
+ begin
45
+ File.delete(TMP_FILE)
46
+ rescue Errno::ENOENT
47
+ nil
48
+ end
49
+ end
44
50
  File.delete(OUTPUT) if File.file?(OUTPUT)
45
51
  end
46
52
 
@@ -18,6 +18,16 @@ describe "An empty ConnectionPool" do
18
18
  @cpool = Sequel::ConnectionPool.get_pool(mock_db.call, CONNECTION_POOL_DEFAULTS)
19
19
  end
20
20
 
21
+ deprecated "should support :pool_class option with string" do
22
+ begin
23
+ c = Class.new(Sequel::ConnectionPool)
24
+ Sequel::ConnectionPool::CONNECTION_POOL__MAP[:foo] = c
25
+ Sequel::ConnectionPool.get_pool(mock_db.call, :pool_class=>:foo).must_be_instance_of c
26
+ ensure
27
+ Sequel::ConnectionPool::CONNECTION_POOL__MAP.delete(c)
28
+ end
29
+ end
30
+
21
31
  it "should have no available connections" do
22
32
  @cpool.available_connections.must_equal []
23
33
  end
@@ -26,9 +36,13 @@ describe "An empty ConnectionPool" do
26
36
  @cpool.allocated.must_equal({})
27
37
  end
28
38
 
29
- it "should have a created_count of zero" do
39
+ deprecated "should have a created_count of zero" do
30
40
  @cpool.created_count.must_equal 0
31
41
  end
42
+
43
+ it "should have a size of zero" do
44
+ @cpool.size.must_equal 0
45
+ end
32
46
  end
33
47
 
34
48
  describe "ConnectionPool options" do
@@ -54,11 +68,11 @@ describe "A connection pool handling connections" do
54
68
  @cpool = Sequel::ConnectionPool.get_pool(mock_db.call(proc{|c| msp.call}){:got_connection}, CONNECTION_POOL_DEFAULTS.merge(:max_connections=>@max_size))
55
69
  end
56
70
 
57
- it "#hold should increment #created_count" do
71
+ it "#hold should increment #size" do
58
72
  @cpool.hold do
59
- @cpool.created_count.must_equal 1
60
- @cpool.hold {@cpool.hold {@cpool.created_count.must_equal 1}}
61
- Thread.new{@cpool.hold {_(@cpool.created_count).must_equal 2}}.join
73
+ @cpool.size.must_equal 1
74
+ @cpool.hold {@cpool.hold {@cpool.size.must_equal 1}}
75
+ Thread.new{@cpool.hold {_(@cpool.size).must_equal 2}}.join
62
76
  end
63
77
  end
64
78
 
@@ -100,7 +114,7 @@ describe "A connection pool handling connections" do
100
114
  q = Queue.new
101
115
  50.times{Thread.new{@cpool.hold{q.pop}}}
102
116
  50.times{q.push nil}
103
- @cpool.created_count.must_be :<=, @max_size
117
+ @cpool.size.must_be :<=, @max_size
104
118
  end
105
119
 
106
120
  it "database's disconnect connection method should be called when a disconnect is detected" do
@@ -110,16 +124,16 @@ describe "A connection pool handling connections" do
110
124
  end
111
125
 
112
126
  it "#hold should remove the connection if a DatabaseDisconnectError is raised" do
113
- @cpool.created_count.must_equal 0
127
+ @cpool.size.must_equal 0
114
128
  q, q1 = Queue.new, Queue.new
115
129
  @cpool.hold{Thread.new{@cpool.hold{q1.pop; q.push nil}; q1.pop; q.push nil}; q1.push nil; q.pop; q1.push nil; q.pop}
116
- @cpool.created_count.must_equal 2
130
+ @cpool.size.must_equal 2
117
131
  proc{@cpool.hold{raise Sequel::DatabaseDisconnectError}}.must_raise(Sequel::DatabaseDisconnectError)
118
- @cpool.created_count.must_equal 1
132
+ @cpool.size.must_equal 1
119
133
  proc{@cpool.hold{raise Sequel::DatabaseDisconnectError}}.must_raise(Sequel::DatabaseDisconnectError)
120
- @cpool.created_count.must_equal 0
134
+ @cpool.size.must_equal 0
121
135
  proc{@cpool.hold{raise Sequel::DatabaseDisconnectError}}.must_raise(Sequel::DatabaseDisconnectError)
122
- @cpool.created_count.must_equal 0
136
+ @cpool.size.must_equal 0
123
137
  end
124
138
  end
125
139
 
@@ -127,13 +141,13 @@ describe "A connection pool handling connection errors" do
127
141
  it "#hold should raise a Sequel::DatabaseConnectionError if an exception is raised by the connection_proc" do
128
142
  cpool = Sequel::ConnectionPool.get_pool(mock_db.call{raise Interrupt}, CONNECTION_POOL_DEFAULTS)
129
143
  proc{cpool.hold{:block_return}}.must_raise(Sequel::DatabaseConnectionError)
130
- cpool.created_count.must_equal 0
144
+ cpool.size.must_equal 0
131
145
  end
132
146
 
133
147
  it "#hold should raise a Sequel::DatabaseConnectionError if nil is returned by the connection_proc" do
134
148
  cpool = Sequel::ConnectionPool.get_pool(mock_db.call{nil}, CONNECTION_POOL_DEFAULTS)
135
149
  proc{cpool.hold{:block_return}}.must_raise(Sequel::DatabaseConnectionError)
136
- cpool.created_count.must_equal 0
150
+ cpool.size.must_equal 0
137
151
  end
138
152
  end
139
153
 
@@ -1100,7 +1114,7 @@ AllConnectionPoolClassesSpecs = shared_description do
1100
1114
  end
1101
1115
  end
1102
1116
 
1103
- Sequel::ConnectionPool::CONNECTION_POOL_MAP.keys.each do |k, v|
1117
+ Sequel::ConnectionPool::CONNECTION_POOL__MAP.keys.each do |k, v|
1104
1118
  opts = {:single_threaded=>k, :servers=>(v ? {} : nil)}
1105
1119
  describe "Connection pool with #{opts.inspect}" do
1106
1120
  before(:all) do
@@ -517,6 +517,130 @@ describe "Database#extend_datasets" do
517
517
  end
518
518
  end
519
519
 
520
+ describe "Database#extend_datasets custom methods" do
521
+ before do
522
+ @db = Sequel.mock
523
+ end
524
+
525
+ def ds
526
+ @db[:items]
527
+ end
528
+
529
+ it "should have dataset_module support a where method" do
530
+ @db.extend_datasets{where :released, :released}
531
+ ds.released.sql.must_equal 'SELECT * FROM items WHERE released'
532
+ ds.where(:foo).released.sql.must_equal 'SELECT * FROM items WHERE (foo AND released)'
533
+ end
534
+
535
+ it "should have dataset_module support a having method" do
536
+ @db.extend_datasets{having(:released){released}}
537
+ ds.released.sql.must_equal 'SELECT * FROM items HAVING released'
538
+ ds.where(:foo).released.sql.must_equal 'SELECT * FROM items WHERE foo HAVING released'
539
+ end
540
+
541
+ it "should have dataset_module support an exclude method" do
542
+ @db.extend_datasets{exclude :released, :released}
543
+ ds.released.sql.must_equal 'SELECT * FROM items WHERE NOT released'
544
+ ds.where(:foo).released.sql.must_equal 'SELECT * FROM items WHERE (foo AND NOT released)'
545
+ end
546
+
547
+ it "should have dataset_module support an exclude_having method" do
548
+ @db.extend_datasets{exclude_having :released, :released}
549
+ ds.released.sql.must_equal 'SELECT * FROM items HAVING NOT released'
550
+ ds.where(:foo).released.sql.must_equal 'SELECT * FROM items WHERE foo HAVING NOT released'
551
+ end
552
+
553
+ it "should have dataset_module support a distinct method" do
554
+ @db.extend_datasets{def supports_distinct_on?; true end; distinct :foo, :baz}
555
+ ds.foo.sql.must_equal 'SELECT DISTINCT ON (baz) * FROM items'
556
+ ds.where(:bar).foo.sql.must_equal 'SELECT DISTINCT ON (baz) * FROM items WHERE bar'
557
+ end
558
+
559
+ it "should have dataset_module support a grep method" do
560
+ @db.extend_datasets{grep :foo, :baz, 'quux%'}
561
+ ds.foo.sql.must_equal 'SELECT * FROM items WHERE ((baz LIKE \'quux%\' ESCAPE \'\\\'))'
562
+ ds.where(:bar).foo.sql.must_equal 'SELECT * FROM items WHERE (bar AND ((baz LIKE \'quux%\' ESCAPE \'\\\')))'
563
+ end
564
+
565
+ it "should have dataset_module support a group method" do
566
+ @db.extend_datasets{group :foo, :baz}
567
+ ds.foo.sql.must_equal 'SELECT * FROM items GROUP BY baz'
568
+ ds.where(:bar).foo.sql.must_equal 'SELECT * FROM items WHERE bar GROUP BY baz'
569
+ end
570
+
571
+ it "should have dataset_module support a group_and_count method" do
572
+ @db.extend_datasets{group_and_count :foo, :baz}
573
+ ds.foo.sql.must_equal 'SELECT baz, count(*) AS count FROM items GROUP BY baz'
574
+ ds.where(:bar).foo.sql.must_equal 'SELECT baz, count(*) AS count FROM items WHERE bar GROUP BY baz'
575
+ end
576
+
577
+ it "should have dataset_module support a group_append method" do
578
+ @db.extend_datasets{group_append :foo, :baz}
579
+ ds.foo.sql.must_equal 'SELECT * FROM items GROUP BY baz'
580
+ ds.group(:bar).foo.sql.must_equal 'SELECT * FROM items GROUP BY bar, baz'
581
+ end
582
+
583
+ it "should have dataset_module support a limit method" do
584
+ @db.extend_datasets{limit :foo, 1}
585
+ ds.foo.sql.must_equal 'SELECT * FROM items LIMIT 1'
586
+ ds.where(:bar).foo.sql.must_equal 'SELECT * FROM items WHERE bar LIMIT 1'
587
+ end
588
+
589
+ it "should have dataset_module support a offset method" do
590
+ @db.extend_datasets{offset :foo, 1}
591
+ ds.foo.sql.must_equal 'SELECT * FROM items OFFSET 1'
592
+ ds.where(:bar).foo.sql.must_equal 'SELECT * FROM items WHERE bar OFFSET 1'
593
+ end
594
+
595
+ it "should have dataset_module support a order method" do
596
+ @db.extend_datasets{order(:foo){:baz}}
597
+ ds.foo.sql.must_equal 'SELECT * FROM items ORDER BY baz'
598
+ ds.where(:bar).foo.sql.must_equal 'SELECT * FROM items WHERE bar ORDER BY baz'
599
+ end
600
+
601
+ it "should have dataset_module support a order_append method" do
602
+ @db.extend_datasets{order_append :foo, :baz}
603
+ ds.foo.sql.must_equal 'SELECT * FROM items ORDER BY baz'
604
+ ds.order(:bar).foo.sql.must_equal 'SELECT * FROM items ORDER BY bar, baz'
605
+ end
606
+
607
+ it "should have dataset_module support a order_prepend method" do
608
+ @db.extend_datasets{order_prepend :foo, :baz}
609
+ ds.foo.sql.must_equal 'SELECT * FROM items ORDER BY baz'
610
+ ds.order(:bar).foo.sql.must_equal 'SELECT * FROM items ORDER BY baz, bar'
611
+ end
612
+
613
+ it "should have dataset_module support a select method" do
614
+ @db.extend_datasets{select :foo, :baz}
615
+ ds.foo.sql.must_equal 'SELECT baz FROM items'
616
+ ds.where(:bar).foo.sql.must_equal 'SELECT baz FROM items WHERE bar'
617
+ end
618
+
619
+ it "should have dataset_module support a select_all method" do
620
+ @db.extend_datasets{select_all :foo, :baz}
621
+ ds.foo.sql.must_equal 'SELECT baz.* FROM items'
622
+ ds.where(:bar).foo.sql.must_equal 'SELECT baz.* FROM items WHERE bar'
623
+ end
624
+
625
+ it "should have dataset_module support a select_append method" do
626
+ @db.extend_datasets{select_append :foo, :baz}
627
+ ds.foo.sql.must_equal 'SELECT *, baz FROM items'
628
+ ds.where(:bar).foo.sql.must_equal 'SELECT *, baz FROM items WHERE bar'
629
+ end
630
+
631
+ it "should have dataset_module support a select_group method" do
632
+ @db.extend_datasets{select_group :foo, :baz}
633
+ ds.foo.sql.must_equal 'SELECT baz FROM items GROUP BY baz'
634
+ ds.where(:bar).foo.sql.must_equal 'SELECT baz FROM items WHERE bar GROUP BY baz'
635
+ end
636
+
637
+ it "should have dataset_module support a server method" do
638
+ @db.extend_datasets{server :foo, :baz}
639
+ ds.foo.opts[:server].must_equal :baz
640
+ ds.where(:bar).foo.opts[:server].must_equal :baz
641
+ end
642
+ end
643
+
520
644
  describe "Database#disconnect_connection" do
521
645
  it "should call close on the connection" do
522
646
  o = Object.new
@@ -2257,6 +2381,31 @@ describe "Database#typecast_value" do
2257
2381
  end
2258
2382
  end
2259
2383
 
2384
+ it "should have an underlying exception class available at cause" do
2385
+ begin
2386
+ @db.typecast_value(:date, 'a')
2387
+ true.must_equal false
2388
+ rescue Sequel::InvalidValue => e
2389
+ e.cause.must_be_kind_of(ArgumentError)
2390
+ end
2391
+ end if RUBY_VERSION >= '2.1'
2392
+
2393
+ it "should have an underlying exception class available at cause when using nested exceptions" do
2394
+ begin
2395
+ begin
2396
+ raise ArgumentError
2397
+ rescue => e1
2398
+ begin
2399
+ raise RuntimeError
2400
+ rescue => e2
2401
+ @db.send(:raise_error, e1)
2402
+ end
2403
+ end
2404
+ rescue Sequel::DatabaseError => e
2405
+ e.cause.must_be_kind_of(ArgumentError)
2406
+ end
2407
+ end if RUBY_VERSION >= '2.1'
2408
+
2260
2409
  it "should include underlying exception class in #inspect" do
2261
2410
  begin
2262
2411
  @db.typecast_value(:date, 'a')