sequel 3.43.0 → 3.44.0

Sign up to get free protection for your applications and to get access to all the features.
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
+