sequel 3.43.0 → 3.44.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 (73) hide show
  1. data/CHANGELOG +32 -0
  2. data/doc/association_basics.rdoc +10 -3
  3. data/doc/release_notes/3.37.0.txt +1 -1
  4. data/doc/release_notes/3.44.0.txt +152 -0
  5. data/lib/sequel/adapters/ado.rb +0 -6
  6. data/lib/sequel/adapters/db2.rb +0 -3
  7. data/lib/sequel/adapters/dbi.rb +0 -6
  8. data/lib/sequel/adapters/ibmdb.rb +0 -3
  9. data/lib/sequel/adapters/jdbc.rb +8 -12
  10. data/lib/sequel/adapters/jdbc/as400.rb +2 -18
  11. data/lib/sequel/adapters/jdbc/derby.rb +10 -0
  12. data/lib/sequel/adapters/jdbc/h2.rb +10 -0
  13. data/lib/sequel/adapters/jdbc/hsqldb.rb +10 -0
  14. data/lib/sequel/adapters/jdbc/sqlite.rb +5 -0
  15. data/lib/sequel/adapters/jdbc/sqlserver.rb +2 -4
  16. data/lib/sequel/adapters/mock.rb +5 -0
  17. data/lib/sequel/adapters/mysql2.rb +4 -13
  18. data/lib/sequel/adapters/odbc.rb +0 -5
  19. data/lib/sequel/adapters/oracle.rb +3 -5
  20. data/lib/sequel/adapters/postgres.rb +2 -1
  21. data/lib/sequel/adapters/shared/access.rb +10 -0
  22. data/lib/sequel/adapters/shared/cubrid.rb +9 -0
  23. data/lib/sequel/adapters/shared/db2.rb +10 -0
  24. data/lib/sequel/adapters/shared/mssql.rb +10 -0
  25. data/lib/sequel/adapters/shared/mysql.rb +14 -0
  26. data/lib/sequel/adapters/shared/oracle.rb +15 -0
  27. data/lib/sequel/adapters/shared/postgres.rb +26 -2
  28. data/lib/sequel/adapters/shared/sqlite.rb +15 -0
  29. data/lib/sequel/adapters/tinytds.rb +5 -32
  30. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +2 -37
  31. data/lib/sequel/core.rb +3 -3
  32. data/lib/sequel/database/misc.rb +40 -4
  33. data/lib/sequel/database/query.rb +1 -1
  34. data/lib/sequel/database/schema_methods.rb +33 -12
  35. data/lib/sequel/dataset/actions.rb +51 -2
  36. data/lib/sequel/dataset/features.rb +0 -6
  37. data/lib/sequel/dataset/sql.rb +1 -1
  38. data/lib/sequel/exceptions.rb +22 -7
  39. data/lib/sequel/extensions/columns_introspection.rb +30 -5
  40. data/lib/sequel/extensions/pg_auto_parameterize.rb +9 -0
  41. data/lib/sequel/model/associations.rb +50 -37
  42. data/lib/sequel/model/base.rb +30 -1
  43. data/lib/sequel/plugins/eager_each.rb +17 -21
  44. data/lib/sequel/plugins/identity_map.rb +2 -1
  45. data/lib/sequel/plugins/many_through_many.rb +1 -1
  46. data/lib/sequel/plugins/single_table_inheritance.rb +2 -2
  47. data/lib/sequel/plugins/tactical_eager_loading.rb +1 -1
  48. data/lib/sequel/sql.rb +4 -2
  49. data/lib/sequel/version.rb +1 -1
  50. data/spec/adapters/postgres_spec.rb +32 -2
  51. data/spec/adapters/sqlite_spec.rb +20 -0
  52. data/spec/core/database_spec.rb +40 -0
  53. data/spec/core/dataset_spec.rb +91 -4
  54. data/spec/core/mock_adapter_spec.rb +2 -1
  55. data/spec/core/schema_generator_spec.rb +4 -0
  56. data/spec/core/schema_spec.rb +9 -3
  57. data/spec/extensions/association_dependencies_spec.rb +3 -3
  58. data/spec/extensions/columns_introspection_spec.rb +28 -2
  59. data/spec/extensions/eager_each_spec.rb +0 -1
  60. data/spec/extensions/identity_map_spec.rb +3 -2
  61. data/spec/extensions/migration_spec.rb +6 -0
  62. data/spec/extensions/prepared_statements_associations_spec.rb +2 -2
  63. data/spec/extensions/rcte_tree_spec.rb +2 -2
  64. data/spec/extensions/single_table_inheritance_spec.rb +3 -0
  65. data/spec/extensions/tactical_eager_loading_spec.rb +1 -1
  66. data/spec/extensions/validation_class_methods_spec.rb +8 -0
  67. data/spec/integration/associations_test.rb +4 -4
  68. data/spec/integration/database_test.rb +68 -20
  69. data/spec/integration/dataset_test.rb +48 -0
  70. data/spec/integration/schema_test.rb +25 -1
  71. data/spec/model/associations_spec.rb +21 -8
  72. data/spec/model/dataset_methods_spec.rb +58 -18
  73. metadata +4 -2
@@ -1934,6 +1934,35 @@ module Sequel
1934
1934
  end
1935
1935
  end
1936
1936
 
1937
+ # If there is no order already defined on this dataset, order it by
1938
+ # the primary key and call last.
1939
+ #
1940
+ # Album.last
1941
+ # # SELECT * FROM albums ORDER BY id DESC LIMIT 1
1942
+ def last(*a, &block)
1943
+ if opts[:order].nil? && model && (pk = model.primary_key)
1944
+ order(*pk).last(*a, &block)
1945
+ else
1946
+ super
1947
+ end
1948
+ end
1949
+
1950
+ # If there is no order already defined on this dataset, order it by
1951
+ # the primary key and call paged_each.
1952
+ #
1953
+ # Album.paged_each{|row| ...}
1954
+ # # SELECT * FROM albums ORDER BY id LIMIT 1000 OFFSET 0
1955
+ # # SELECT * FROM albums ORDER BY id LIMIT 1000 OFFSET 1000
1956
+ # # SELECT * FROM albums ORDER BY id LIMIT 1000 OFFSET 2000
1957
+ # # ...
1958
+ def paged_each(*a, &block)
1959
+ if opts[:order].nil? && model && (pk = model.primary_key)
1960
+ order(*pk).paged_each(*a, &block)
1961
+ else
1962
+ super
1963
+ end
1964
+ end
1965
+
1937
1966
  # This allows you to call +to_hash+ without any arguments, which will
1938
1967
  # result in a hash with the primary key value being the key and the
1939
1968
  # model object being the value.
@@ -1946,7 +1975,7 @@ module Sequel
1946
1975
  if key_column
1947
1976
  super
1948
1977
  else
1949
- raise(Sequel::Error, "No primary key for model") unless model and pk = model.primary_key
1978
+ raise(Sequel::Error, "No primary key for model") unless model && (pk = model.primary_key)
1950
1979
  super(pk, value_column)
1951
1980
  end
1952
1981
  end
@@ -12,7 +12,7 @@ module Sequel
12
12
  # #each, this is a bit of issue, but this plugin resolves the issue by cloning the dataset
13
13
  # and setting a new flag in the cloned dataset, so that each can check with the flag to
14
14
  # determine whether it should call all.
15
- #
15
+ #
16
16
  # Usage:
17
17
  #
18
18
  # # Make all model subclass instances eagerly load for each (called before loading subclasses)
@@ -21,37 +21,33 @@ module Sequel
21
21
  # # Make the Album class eagerly load for each
22
22
  # Album.plugin :eager_each
23
23
  module EagerEach
24
- # Methods added to eagerly loaded datasets when the eager_each plugin is in use.
25
- module EagerDatasetMethods
26
- # Call #all instead of #each unless #each is being called by #all.
24
+ module DatasetMethods
25
+ # Call #all instead of #each if eager loading,
26
+ # uless #each is being called by #all.
27
27
  def each(&block)
28
- if opts[:all_called]
29
- super
30
- else
28
+ if use_eager_all?
31
29
  all(&block)
30
+ else
31
+ super
32
32
  end
33
33
  end
34
34
 
35
- # Clone the dataset and set a flag to let #each know not to call #all,
35
+ # If eager loading, clone the dataset and set a flag to let #each know not to call #all,
36
36
  # to avoid the infinite loop.
37
37
  def all(&block)
38
- if opts[:all_called]
39
- super
40
- else
38
+ if use_eager_all?
41
39
  clone(:all_called=>true).all(&block)
40
+ else
41
+ super
42
42
  end
43
43
  end
44
- end
45
44
 
46
- module DatasetMethods
47
- # Make sure calling each on this dataset will eagerly load the dataset.
48
- def eager(*)
49
- super.extend(EagerDatasetMethods)
50
- end
51
-
52
- # Make sure calling each on this dataset will eagerly load the dataset.
53
- def eager_graph(*)
54
- super.extend(EagerDatasetMethods)
45
+ private
46
+
47
+ # Wether to use all when each is called, true when eager loading
48
+ # unless the flag has already been set.
49
+ def use_eager_all?
50
+ (opts[:eager] || opts[:eager_graph]) && !opts[:all_called]
55
51
  end
56
52
  end
57
53
  end
@@ -36,7 +36,7 @@ module Sequel
36
36
  module ClassMethods
37
37
  # Override the default :eager_loader option for many_*_many associations to work
38
38
  # with an identity_map. If the :eager_graph association option is used, you'll probably have to use
39
- # :uniq=>true on the current association amd :cartesian_product_number=>2 on the association
39
+ # :uniq=>true on the current association and :cartesian_product_number=>2 on the association
40
40
  # mentioned by :eager_graph, otherwise you'll end up with duplicates because the row proc will be
41
41
  # getting called multiple times for the same object. If you do have duplicates and you use :eager_graph,
42
42
  # they'll probably be lost. Making that work correctly would require changing a lot of the core
@@ -52,6 +52,7 @@ module Sequel
52
52
  uses_lcks = opts[:uses_left_composite_keys]
53
53
  uses_rcks = opts[:uses_right_composite_keys]
54
54
  right = opts[:right_key]
55
+ rcks = opts[:right_keys]
55
56
  join_table = opts[:join_table]
56
57
  left = opts[:left_key]
57
58
  lcks = opts[:left_keys]
@@ -192,7 +192,7 @@ module Sequel
192
192
  lpkc = opts[:left_primary_key_column] ||= left_pk
193
193
  lpkcs = opts[:left_primary_key_columns] ||= Array(lpkc)
194
194
  opts[:dataset] ||= lambda do
195
- ds = opts.associated_class
195
+ ds = opts.associated_dataset
196
196
  opts.reverse_edges.each{|t| ds = ds.join(t[:table], Array(t[:left]).zip(Array(t[:right])), :table_alias=>t[:alias], :qualify=>:deep)}
197
197
  ft = opts.final_reverse_edge
198
198
  ds.join(ft[:table], Array(ft[:left]).zip(Array(ft[:right])) + opts.predicate_keys.zip(left_pks.map{|k| send(k)}), :table_alias=>ft[:alias], :qualify=>:deep)
@@ -59,8 +59,8 @@ module Sequel
59
59
  # # which are different but do not require different code.
60
60
  # Employee.plugin :single_table_inheritance, :type,
61
61
  # :model_map=>{'staff' => "Staff",
62
- # 'manager => "Manager",
63
- # 'overpayed staff => "Staff",
62
+ # 'manager' => "Manager",
63
+ # 'overpayed staff' => "Staff",
64
64
  # 'underpayed staff' => "Staff"}
65
65
  #
66
66
  # One minor issue to note is that if you specify the <tt>:key_map</tt>
@@ -60,7 +60,7 @@ module Sequel
60
60
  module DatasetMethods
61
61
  private
62
62
 
63
- # Set the reteived_with and retrieved_by attributes for the object
63
+ # Set the retrieved_with and retrieved_by attributes for the object
64
64
  # with the current dataset and array of all objects.
65
65
  def post_load(objects)
66
66
  super
data/lib/sequel/sql.rb CHANGED
@@ -1497,8 +1497,8 @@ module Sequel
1497
1497
  #
1498
1498
  # An instance of this class is yielded to the block supplied to <tt>Dataset#filter</tt>, <tt>Dataset#order</tt>, and <tt>Dataset#select</tt>
1499
1499
  # (and the other methods that accept a block and pass it to one of those methods).
1500
- # If the block doesn't take an argument, the block is instance_evaled in the context of
1501
- # a new instance of this class.
1500
+ # If the block doesn't take an argument, the block is instance_execed in the context of
1501
+ # an instance of this class.
1502
1502
  #
1503
1503
  # +VirtualRow+ uses +method_missing+ to return either an +Identifier+, +QualifiedIdentifier+, +Function+, or +WindowFunction+,
1504
1504
  # depending on how it is called.
@@ -1621,6 +1621,8 @@ module Sequel
1621
1621
  Function.new(m, *args)
1622
1622
  end
1623
1623
  end
1624
+
1625
+ Sequel::VIRTUAL_ROW = new
1624
1626
  end
1625
1627
 
1626
1628
  # A +Window+ is part of a window function specifying the window over which the function operates.
@@ -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 = 43
6
+ MINOR = 44
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
@@ -48,6 +48,28 @@ describe "PostgreSQL", '#create_table' do
48
48
  end
49
49
  end
50
50
 
51
+ describe "PostgreSQL temporary views" do
52
+ before do
53
+ @db = POSTGRES_DB
54
+ @db.drop_view(:items_view) rescue nil
55
+ @db.create_table!(:items){Integer :number}
56
+ @db[:items].insert(10)
57
+ @db[:items].insert(20)
58
+ end
59
+ after do
60
+ @db.drop_table?(:items)
61
+ end
62
+
63
+ specify "should be supported" do
64
+ @db.create_view(:items_view, @db[:items].where(:number=>10), :temp=>true)
65
+ @db[:items_view].map(:number).should == [10]
66
+ @db.create_or_replace_view(:items_view, @db[:items].where(:number=>20), :temp=>true)
67
+ @db[:items_view].map(:number).should == [20]
68
+ @db.disconnect
69
+ lambda{@db[:items_view].map(:number)}.should raise_error(Sequel::DatabaseError)
70
+ end
71
+ end unless POSTGRES_DB.adapter_scheme == :do # Causes freezing later
72
+
51
73
  describe "A PostgreSQL database" do
52
74
  before(:all) do
53
75
  @db = POSTGRES_DB
@@ -61,6 +83,14 @@ describe "A PostgreSQL database" do
61
83
  @db.server_version.should > 70000
62
84
  end
63
85
 
86
+ specify "should not typecast the int2vector type incorrectly" do
87
+ @db.get(Sequel.cast('10 20', :int2vector)).should_not == 10
88
+ end
89
+
90
+ cspecify "should not typecast the money type incorrectly", :do do
91
+ @db.get(Sequel.cast('10.01', :money)).should_not == 0
92
+ end
93
+
64
94
  specify "should correctly parse the schema" do
65
95
  @db.schema(:public__testfk, :reload=>true).should == [
66
96
  [:id, {:type=>:integer, :ruby_default=>nil, :db_type=>"integer", :default=>"nextval('testfk_id_seq'::regclass)", :oid=>23, :primary_key=>true, :allow_null=>false}],
@@ -149,13 +179,13 @@ describe "A PostgreSQL dataset" do
149
179
  @db.create_table!(:atest){Integer :t; exclude [[Sequel.desc(:t, :nulls=>:last), '=']], :using=>:btree, :where=>proc{t > 0}}
150
180
  @db[:atest].insert(1)
151
181
  @db[:atest].insert(2)
152
- proc{@db[:atest].insert(2)}.should raise_error(Sequel::DatabaseError)
182
+ proc{@db[:atest].insert(2)}.should raise_error(Sequel::Postgres::ExclusionConstraintViolation)
153
183
 
154
184
  @db.create_table!(:atest){Integer :t}
155
185
  @db.alter_table(:atest){add_exclusion_constraint [[:t, '=']], :using=>:btree, :name=>'atest_ex'}
156
186
  @db[:atest].insert(1)
157
187
  @db[:atest].insert(2)
158
- proc{@db[:atest].insert(2)}.should raise_error(Sequel::DatabaseError)
188
+ proc{@db[:atest].insert(2)}.should raise_error(Sequel::Postgres::ExclusionConstraintViolation)
159
189
  @db.alter_table(:atest){drop_constraint 'atest_ex'}
160
190
  end if POSTGRES_DB.server_version >= 90000
161
191
 
@@ -175,6 +175,26 @@ describe "An SQLite database" do
175
175
  end
176
176
  end
177
177
 
178
+ describe "SQLite temporary views" do
179
+ before do
180
+ @db = SQLITE_DB
181
+ @db.drop_view(:items) rescue nil
182
+ @db.create_table!(:items){Integer :number}
183
+ @db[:items].insert(10)
184
+ @db[:items].insert(20)
185
+ end
186
+ after do
187
+ @db.drop_table?(:items)
188
+ end
189
+
190
+ specify "should be supported" do
191
+ @db.create_view(:items_view, @db[:items].where(:number=>10), :temp=>true)
192
+ @db[:items_view].map(:number).should == [10]
193
+ @db.disconnect
194
+ lambda{@db[:items_view].map(:number)}.should raise_error(Sequel::DatabaseError)
195
+ end
196
+ end
197
+
178
198
  describe "SQLite type conversion" do
179
199
  before do
180
200
  @db = SQLITE_DB
@@ -479,6 +479,26 @@ describe "Database#extend_datasets" do
479
479
  end
480
480
  end
481
481
 
482
+ describe "Database#disconnect_connection" do
483
+ specify "should call close on the connection" do
484
+ o = Object.new
485
+ def o.close() @closed=true end
486
+ Sequel::Database.new.disconnect_connection(o)
487
+ o.instance_variable_get(:@closed).should be_true
488
+ end
489
+ end
490
+
491
+ describe "Database#valid_connection?" do
492
+ specify "should issue a query to validate the connection" do
493
+ db = Sequel.mock
494
+ db.synchronize{|c| db.valid_connection?(c)}.should be_true
495
+ db.synchronize do |c|
496
+ def c.execute(*) raise Sequel::DatabaseError, "error" end
497
+ db.valid_connection?(c)
498
+ end.should be_false
499
+ end
500
+ end
501
+
482
502
  describe "Database#execute" do
483
503
  specify "should raise Sequel::NotImplemented" do
484
504
  proc {Sequel::Database.new.execute('blah blah')}.should raise_error(Sequel::NotImplemented)
@@ -1649,6 +1669,14 @@ describe "Database#raise_error" do
1649
1669
  specify "should convert the exception to a DatabaseDisconnectError if opts[:disconnect] is true" do
1650
1670
  proc{@db.send(:raise_error, Interrupt.new(''), :disconnect=>true)}.should raise_error(Sequel::DatabaseDisconnectError)
1651
1671
  end
1672
+
1673
+ specify "should convert the exception to an appropriate error if exception message matches regexp" do
1674
+ def @db.database_error_regexps
1675
+ {/foo/ => Sequel::DatabaseDisconnectError, /bar/ => Sequel::ConstraintViolation}
1676
+ end
1677
+ proc{@db.send(:raise_error, Interrupt.new('foo'))}.should raise_error(Sequel::DatabaseDisconnectError)
1678
+ proc{@db.send(:raise_error, Interrupt.new('bar'))}.should raise_error(Sequel::ConstraintViolation)
1679
+ end
1652
1680
  end
1653
1681
 
1654
1682
  describe "Database#typecast_value" do
@@ -2018,6 +2046,18 @@ describe "Database#schema_autoincrementing_primary_key?" do
2018
2046
  end
2019
2047
  end
2020
2048
 
2049
+ describe "Database#supports_deferrable_constraints?" do
2050
+ specify "should be false by default" do
2051
+ Sequel::Database.new.supports_deferrable_constraints?.should == false
2052
+ end
2053
+ end
2054
+
2055
+ describe "Database#supports_deferrable_foreign_key_constraints?" do
2056
+ specify "should be false by default" do
2057
+ Sequel::Database.new.supports_deferrable_foreign_key_constraints?.should == false
2058
+ end
2059
+ end
2060
+
2021
2061
  describe "Database#supports_transactional_ddl?" do
2022
2062
  specify "should be false by default" do
2023
2063
  Sequel::Database.new.supports_transactional_ddl?.should == false
@@ -40,6 +40,14 @@ describe "Dataset" do
40
40
  Sequel::Dataset.included_modules.should include(Enumerable)
41
41
  end
42
42
 
43
+ specify "should yield rows to each" do
44
+ ds = Sequel.mock[:t]
45
+ ds._fetch = {:x=>1}
46
+ called = false
47
+ ds.each{|a| called = true; a.should == {:x=>1}}
48
+ called.should be_true
49
+ end
50
+
43
51
  specify "should get quote_identifiers default from database" do
44
52
  db = Sequel::Database.new(:quote_identifiers=>true)
45
53
  db[:a].quote_identifiers?.should == true
@@ -4359,10 +4367,6 @@ describe "Dataset feature defaults" do
4359
4367
  it "should not require placeholder type specifiers by default" do
4360
4368
  Sequel::Database.new.dataset.requires_placeholder_type_specifiers?.should be_false
4361
4369
  end
4362
-
4363
- it "offset use should be returning a separate row number column by default" do
4364
- Sequel::Database.new.dataset.send(:offset_returns_row_number_column?).should be_false
4365
- end
4366
4370
  end
4367
4371
 
4368
4372
  describe "Dataset extensions" do
@@ -4465,6 +4469,11 @@ describe "Dataset#schema_and_table" do
4465
4469
  @ds.schema_and_table('s').should == [nil, 's']
4466
4470
  end
4467
4471
 
4472
+ it "should correctly handle literal strings" do
4473
+ s = Sequel.lit('s')
4474
+ @ds.schema_and_table(s).last.should equal(s)
4475
+ end
4476
+
4468
4477
  it "should correctly handle identifiers" do
4469
4478
  @ds.schema_and_table(Sequel.identifier(:s)).should == [nil, 's']
4470
4479
  end
@@ -4521,3 +4530,81 @@ describe "Dataset#split_qualifiers" do
4521
4530
  end
4522
4531
  end
4523
4532
 
4533
+ describe "Dataset#paged_each" do
4534
+ before do
4535
+ @ds = Sequel.mock[:test].order(:x)
4536
+ @db = (0...10).map{|i| {:x=>i}}
4537
+ @ds._fetch = @db
4538
+ @rows = []
4539
+ @proc = lambda{|row| @rows << row}
4540
+ end
4541
+
4542
+ it "should yield rows to the passed block" do
4543
+ @ds.paged_each(&@proc)
4544
+ @rows.should == @db
4545
+ end
4546
+
4547
+ it "should respect the row_proc" do
4548
+ @ds.row_proc = lambda{|row| {:x=>row[:x]*2}}
4549
+ @ds.paged_each(&@proc)
4550
+ @rows.should == @db.map{|row| {:x=>row[:x]*2}}
4551
+ end
4552
+
4553
+ it "should use a transaction to ensure consistent results" do
4554
+ @ds.paged_each(&@proc)
4555
+ sqls = @ds.db.sqls
4556
+ sqls[0].should == 'BEGIN'
4557
+ sqls[-1].should == 'COMMIT'
4558
+ end
4559
+
4560
+ it "should use a limit and offset to go through the dataset in chunks at a time" do
4561
+ @ds.paged_each(&@proc)
4562
+ @ds.db.sqls[1...-1].should == ['SELECT * FROM test ORDER BY x LIMIT 1000 OFFSET 0']
4563
+ end
4564
+
4565
+ it "should accept a :rows_per_fetch option to change the number of rows per fetch" do
4566
+ @ds._fetch = @db.each_slice(3).to_a
4567
+ @ds.paged_each(:rows_per_fetch=>3, &@proc)
4568
+ @rows.should == @db
4569
+ @ds.db.sqls[1...-1].should == ['SELECT * FROM test ORDER BY x LIMIT 3 OFFSET 0',
4570
+ 'SELECT * FROM test ORDER BY x LIMIT 3 OFFSET 3',
4571
+ 'SELECT * FROM test ORDER BY x LIMIT 3 OFFSET 6',
4572
+ 'SELECT * FROM test ORDER BY x LIMIT 3 OFFSET 9']
4573
+ end
4574
+
4575
+ it "should handle cases where the last query returns nothing" do
4576
+ @ds._fetch = @db.each_slice(5).to_a
4577
+ @ds.paged_each(:rows_per_fetch=>5, &@proc)
4578
+ @rows.should == @db
4579
+ @ds.db.sqls[1...-1].should == ['SELECT * FROM test ORDER BY x LIMIT 5 OFFSET 0',
4580
+ 'SELECT * FROM test ORDER BY x LIMIT 5 OFFSET 5',
4581
+ 'SELECT * FROM test ORDER BY x LIMIT 5 OFFSET 10']
4582
+ end
4583
+
4584
+ it "should respect an existing server option to use" do
4585
+ @ds = Sequel.mock(:servers=>{:foo=>{}})[:test].order(:x)
4586
+ @ds._fetch = @db
4587
+ @ds.server(:foo).paged_each(&@proc)
4588
+ @rows.should == @db
4589
+ @ds.db.sqls.should == ["BEGIN -- foo", "SELECT * FROM test ORDER BY x LIMIT 1000 OFFSET 0 -- foo", "COMMIT -- foo"]
4590
+ end
4591
+
4592
+ it "should require an order" do
4593
+ lambda{@ds.unordered.paged_each(&@proc)}.should raise_error(Sequel::Error)
4594
+ end
4595
+
4596
+ it "should handle an existing limit and/or offset" do
4597
+ @ds._fetch = @db.each_slice(3).to_a
4598
+ @ds.limit(5).paged_each(:rows_per_fetch=>3, &@proc)
4599
+ @ds.db.sqls[1...-1].should == ["SELECT * FROM test ORDER BY x LIMIT 3 OFFSET 0", "SELECT * FROM test ORDER BY x LIMIT 2 OFFSET 3"]
4600
+
4601
+ @ds._fetch = @db.each_slice(3).to_a
4602
+ @ds.limit(5, 2).paged_each(:rows_per_fetch=>3, &@proc)
4603
+ @ds.db.sqls[1...-1].should == ["SELECT * FROM test ORDER BY x LIMIT 3 OFFSET 2", "SELECT * FROM test ORDER BY x LIMIT 2 OFFSET 5"]
4604
+
4605
+ @ds._fetch = @db.each_slice(3).to_a
4606
+ @ds.limit(nil, 2).paged_each(:rows_per_fetch=>3, &@proc)
4607
+ @ds.db.sqls[1...-1].should == ["SELECT * FROM test ORDER BY x LIMIT 3 OFFSET 2", "SELECT * FROM test ORDER BY x LIMIT 3 OFFSET 5", "SELECT * FROM test ORDER BY x LIMIT 3 OFFSET 8", "SELECT * FROM test ORDER BY x LIMIT 3 OFFSET 11"]
4608
+ end
4609
+ end
4610
+