postgresql_cursor 0.6.1 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: df0425fd30e37c9d61b270416c9892375b0abe69
4
- data.tar.gz: f59a3b1291c6476a16b08b084118be22b76020f5
2
+ SHA256:
3
+ metadata.gz: 8f60d787898c546a7c79c8ff6ca3aee03a54793e9a4e3c0798fc39ad6d6ae7a0
4
+ data.tar.gz: c175f5708bd1f0b1ba06c005254e7fb513d5ea72f9a7b373f16a42d969cb45eb
5
5
  SHA512:
6
- metadata.gz: d5decdc3d118f14eac9d93d46c34bb3b11f51f784bf5ea5a6825aaffe9f85ae23d9094992ef938f363d003435014ef7d771d5d5db835cac22e811248cc881a38
7
- data.tar.gz: 59f0e52d29bccd10d3b9e693d4c40e2920672eefcd3f103309b4f8a4c96d8aa055c9326c03dba11a21b2bb29a05896454d6c37730054c5f85f17c24c1bff23cf
6
+ metadata.gz: 547822d06073ffc68611179e93332d4840f250fe130d4448a0563986e6e41f9e30f26e3894ba326c4981ad51a44bff77bb326f4e4700f3267f80e6fc4e0008d5
7
+ data.tar.gz: 2479ab683e1a598ad18ef5a287106dda78113ab6e745c9ff3bd4eed982c5af9f0a2ad7d1b933b73328229bf39208aa0d25ed513607b06b417742091e7a4c7df6
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- #PostgreSQLCursor for handling large Result Sets
1
+ # PostgreSQLCursor for handling large Result Sets
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/postgresql_cursor.svg)](http://badge.fury.io/rb/postgresql_cursor)
4
4
 
@@ -13,16 +13,12 @@ set is exhausted. By fetching a smaller chunk of data, this reduces the
13
13
  amount of memory your application uses and prevents the potential crash
14
14
  of running out of memory.
15
15
 
16
- This extension is not intended to support the "FOR UPDATE / WHERE
17
- CURRENT OF" syntax to process and update each row in place. The primary
18
- goal is to read a large number of rows using buffering.
19
-
20
16
  Supports Rails/ActiveRecord v3.1 (v3.2 recommended) higher (including
21
17
  v5.0) and Ruby 1.9 and higher. Not all features work in ActiveRecord v3.1.
22
18
  Support for this gem will only be for officially supported versions of
23
19
  ActiveRecord and Ruby; others can try older versions of the gem.
24
20
 
25
- ##Using Cursors
21
+ ## Using Cursors
26
22
 
27
23
  PostgreSQLCursor was developed to take advantage of PostgreSQL's cursors. Cursors allow the program
28
24
  to declare a cursor to run a given query returning "chunks" of rows to the application program while
@@ -79,7 +75,7 @@ Notes:
79
75
  * Aliases each_hash and each_hash_by_sql are provided for each_row and each_row_by_sql
80
76
  if you prefer to express what types are being returned.
81
77
 
82
- ###PostgreSQLCursor is an Enumerable
78
+ ### PostgreSQLCursor is an Enumerable
83
79
 
84
80
  If you do not pass in a block, the cursor is returned, which mixes in the Enumerable
85
81
  libary. With that, you can pass it around, or chain in the awesome enumerable things
@@ -91,7 +87,7 @@ Product.each_row.map {|r| r["id"].to_i } #=> [1, 2, 3, ...]
91
87
  Product.each_instance.map {|r| r.id }.each {|id| p id } #=> [1, 2, 3, ...]
92
88
  Product.each_instance.lazy.inject(0) {|sum,r| sum + r.quantity } #=> 499500
93
89
  ```
94
- ###Hashes vs. Instances
90
+ ### Hashes vs. Instances
95
91
 
96
92
  The each_row method returns the Hash of strings for speed (as this allows you to process a lot of rows).
97
93
  Hashes are returned with String values, and you must take care of any type conversion.
@@ -103,7 +99,7 @@ If you find you need the types cast for your attributes, consider using each_ins
103
99
  insead. ActiveRecord's read casting algorithm will only cast the values you need and
104
100
  has become more efficient over time.
105
101
 
106
- ###Select and Pluck
102
+ ### Select and Pluck
107
103
 
108
104
  To limit the columns returned to just those you need, use `.select(:id, :name)`
109
105
  query method.
@@ -128,7 +124,7 @@ Product.pluck_rows(:id) #=> ["1", "2", ...]
128
124
  Product.pluck_instances(:id, :quantity) #=> [[1, 503], [2, 932], ...]
129
125
  ```
130
126
 
131
- ###Associations and Eager Loading
127
+ ### Associations and Eager Loading
132
128
 
133
129
  ActiveRecord performs some magic when eager-loading associated row. It
134
130
  will usually not join the tables, and prefers to load the data in
@@ -138,7 +134,33 @@ This library hooks onto the `to_sql` feature of the query builder. As a
138
134
  result, it can't do the join if ActiveRecord decided not to join, nor
139
135
  can it construct the association objects eagerly.
140
136
 
141
- ##Background: Why PostgreSQL Cursors?
137
+ ## Locking and Updating Each Row (FOR UPDATE Queries)
138
+
139
+ When you use the AREL `lock` method, a "FOR UPDATE" clause is added to
140
+ the query. This causes the block of rows returned from each FETCH
141
+ operation (see the `block_size` option) to be locked for you to update.
142
+ The lock is released on those rows once the block is exhausted and the
143
+ next FETCH or CLOSE statement is executed.
144
+
145
+ This example will run through a large table and potentially update each
146
+ row, locking only a set of rows at a time to allow concurrent use.
147
+
148
+ ```ruby
149
+ Product.lock.each_instance(block_size:100) do |p|
150
+ p.update(price: p.price * 1.05)
151
+ end
152
+ ```
153
+
154
+ Also, pay attention to the `block_size` you request. Locking large
155
+ blocks of rows for an extended time can cause deadlocks or other
156
+ performance issues in your application. On a busy table, or if the
157
+ processing of each row consumes a lot of time or resources, try a
158
+ `block_size` <= 10.
159
+
160
+ See the [PostgreSQL Select Documentation](https://www.postgresql.org/docs/current/static/sql-select.html)
161
+ for more information and limitations when using "FOR UPDATE" locking.
162
+
163
+ ## Background: Why PostgreSQL Cursors?
142
164
 
143
165
  ActiveRecord is designed and optimized for web performance. In a web transaction, only a "page" of
144
166
  around 20 rows is returned to the user. When you do this
@@ -155,7 +177,7 @@ When there is a very large number of rows, this requires a lot more memory to ho
155
177
  does not return that memory after processing the array, and the causes your process to "bloat". If you
156
178
  don't have enough memory, it will cause an exception.
157
179
 
158
- ###ActiveRecord.find_each and find_in_batches
180
+ ### ActiveRecord.find_each and find_in_batches
159
181
 
160
182
  To solve this problem, ActiveRecord gives us two alternative methods that work in "chunks" of your data:
161
183
 
@@ -179,7 +201,7 @@ There are drawbacks with these methods:
179
201
  ### How it works
180
202
 
181
203
  Under the covers, the library calls the PostgreSQL cursor operations
182
- with the psuedo-code:
204
+ with the pseudo-code:
183
205
 
184
206
  SET cursor_tuple_fraction TO 1.0;
185
207
  DECLARE cursor_1 CURSOR WITH HOLD FOR select * from widgets;
@@ -189,17 +211,11 @@ with the psuedo-code:
189
211
  until rows.size < 100;
190
212
  CLOSE cursor_1;
191
213
 
192
- ##Meta
193
- ###Author
194
- Allen Fair, [@allenfair](https://twitter.com/allenfair), http://github.com/afair
195
-
196
- Thanks to:
197
-
198
- * Iulian Dogariu, http://github.com/iulianu (Fixes)
199
- * Julian Mehnle, julian@mehnle.net (Suggestions)
200
- * ...And all the other contributers!
214
+ ## Meta
215
+ ### Author
216
+ Allen Fair, [@allenfair](https://twitter.com/allenfair), [github://afair](https://github.com/afair)
201
217
 
202
- ###Note on Patches/Pull Requests
218
+ ### Note on Patches/Pull Requests
203
219
 
204
220
  * Fork the project.
205
221
  * Make your feature addition or bug fix.
@@ -209,11 +225,11 @@ Thanks to:
209
225
  (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
210
226
  * Send me a pull request. Bonus points for topic branches.
211
227
 
212
- ###Code of Conduct
228
+ ### Code of Conduct
213
229
 
214
230
  This project adheres to the [Open Code of Conduct](http://todogroup.org/opencodeofconduct/#postgresql_cursor/2016@allenfair.com).
215
231
  By participating, you are expected to honor this code.
216
232
 
217
- ###Copyright
233
+ ### Copyright
218
234
 
219
- Copyright (c) 2010-2014 Allen Fair. See (MIT) LICENSE for details.
235
+ Copyright (c) 2010-2017 Allen Fair. See (MIT) LICENSE for details.
@@ -42,6 +42,47 @@ module PostgreSQLCursor
42
42
  cursor.iterate_type(self)
43
43
  end
44
44
 
45
+ # Public: Executes the query, yielding each batch of up to block_size
46
+ # rows where each row is a hash to the given block.
47
+ #
48
+ # Parameters: same as each_row
49
+ #
50
+ # Example:
51
+ # Post.where(user_id:123).each_row_batch do |batch|
52
+ # Post.process_batch(batch)
53
+ # end
54
+ # Post.each_row_batch.map { |batch| Post.transform_batch(batch) }
55
+ #
56
+ # Returns the number of rows yielded to the block
57
+ def each_row_batch(options={}, &block)
58
+ options = {:connection => self.connection}.merge(options)
59
+ cursor = PostgreSQLCursor::Cursor.new(to_unprepared_sql, options)
60
+ return cursor.each_row_batch(&block) if block_given?
61
+ cursor.iterate_batched
62
+ end
63
+ alias :each_hash_batch :each_row_batch
64
+
65
+ # Public: Like each_row, but yields an array of instantiated model
66
+ # objects to the block
67
+ #
68
+ # Parameters: same as each_row
69
+ #
70
+ # Example:
71
+ # Post.where(user_id:123).each_instance_batch do |batch|
72
+ # Post.process_batch(batch)
73
+ # end
74
+ # Post.where(user_id:123).each_instance_batch.map do |batch|
75
+ # Post.transform_batch(batch)
76
+ # end
77
+ #
78
+ # Returns the number of rows yielded to the block
79
+ def each_instance_batch(options={}, &block)
80
+ options = {:connection => self.connection}.merge(options)
81
+ cursor = PostgreSQLCursor::Cursor.new(to_unprepared_sql, options)
82
+ return cursor.each_instance_batch(self, &block) if block_given?
83
+ cursor.iterate_type(self).iterate_batched
84
+ end
85
+
45
86
  # Plucks the column names from the rows, and return them in an array
46
87
  def pluck_rows(*cols)
47
88
  options = cols.last.is_a?(Hash) ? cols.pop : {}
@@ -74,7 +74,81 @@ module PostgreSQLCursor
74
74
  cursor.iterate_type(self)
75
75
  end
76
76
 
77
- # Returns and array of the given column names. Use if you need cursors and don't expect
77
+ # Public: Executes the query, yielding an array of up to block_size rows
78
+ # where each row is a hash to the given block.
79
+ #
80
+ # Parameters: same as each_row
81
+ #
82
+ # Example:
83
+ # Post.each_row_batch { |batch| Post.process_batch(batch) }
84
+ #
85
+ # Returns the number of rows yielded to the block
86
+ def each_row_batch(options={}, &block)
87
+ options = {:connection => self.connection}.merge(options)
88
+ all.each_row_batch(options, &block)
89
+ end
90
+ alias :each_hash_batch :each_row_batch
91
+
92
+ # Public: Like each_row_batch, but yields an array of instantiated model
93
+ # objects to the block
94
+ #
95
+ # Parameters: same as each_row
96
+ #
97
+ # Example:
98
+ # Post.each_instance_batch { |batch| Post.process_batch(batch) }
99
+ #
100
+ # Returns the number of rows yielded to the block
101
+ def each_instance_batch(options={}, &block)
102
+ options = {:connection => self.connection}.merge(options)
103
+ all.each_instance_batch(options, &block)
104
+ end
105
+
106
+ # Public: Yields each batch of up to block_size rows as an array of rows
107
+ # where each row as a hash to the given block
108
+ #
109
+ # Parameters: see each_row_by_sql
110
+ #
111
+ # Example:
112
+ # Post.each_row_batch_by_sql("select * from posts") do |batch|
113
+ # Post.process_batch(batch)
114
+ # end
115
+ # Post.each_row_batch_by_sql("select * from posts").map do |batch|
116
+ # Post.transform_batch(batch)
117
+ # end
118
+ #
119
+ # Returns the number of rows yielded to the block
120
+ def each_row_batch_by_sql(sql, options={}, &block)
121
+ options = {:connection => self.connection}.merge(options)
122
+ cursor = PostgreSQLCursor::Cursor.new(sql, options)
123
+ return cursor.each_row_batch(&block) if block_given?
124
+ cursor.iterate_batched
125
+ end
126
+ alias :each_hash_batch_by_sql :each_row_batch_by_sql
127
+
128
+ # Public: Yields each batch up to block_size of rows as model instances
129
+ # to the given block
130
+ #
131
+ # As this instantiates a model object, it is slower than each_row_batch_by_sql
132
+ #
133
+ # Paramaters: see each_row_by_sql
134
+ #
135
+ # Example:
136
+ # Post.each_instance_batch_by_sql("select * from posts") do |batch|
137
+ # Post.process_batch(batch)
138
+ # end
139
+ # Post.each_instance_batch_by_sql("select * from posts").map do |batch|
140
+ # Post.transform_batch(batch)
141
+ # end
142
+ #
143
+ # Returns the number of rows yielded to the block
144
+ def each_instance_batch_by_sql(sql, options={}, &block)
145
+ options = {:connection => self.connection}.merge(options)
146
+ cursor = PostgreSQLCursor::Cursor.new(sql, options)
147
+ return cursor.each_instance_batch(self, &block) if block_given?
148
+ cursor.iterate_type(self).iterate_batched
149
+ end
150
+
151
+ # Returns an array of the given column names. Use if you need cursors and don't expect
78
152
  # this to comsume too much memory. Values are strings. Like ActiveRecord's pluck.
79
153
  def pluck_rows(*cols)
80
154
  options = cols.last.is_a?(Hash) ? cols.pop : {}
@@ -82,7 +156,7 @@ module PostgreSQLCursor
82
156
  end
83
157
  alias :pluck_row :pluck_rows
84
158
 
85
- # Returns and array of the given column names. Use if you need cursors and don't expect
159
+ # Returns an array of the given column names. Use if you need cursors and don't expect
86
160
  # this to comsume too much memory. Values are instance types. Like ActiveRecord's pluck.
87
161
  def pluck_instances(*cols)
88
162
  options = cols.last.is_a?(Hash) ? cols.pop : {}
@@ -1,3 +1,5 @@
1
+ require 'active_record/connection_adapters/postgresql/oid'
2
+
1
3
  ################################################################################
2
4
  # PostgreSQLCursor: library class provides postgresql cursor for large result
3
5
  # set processing. Requires ActiveRecord, but can be adapted to other DBI/ORM libraries.
@@ -17,6 +19,14 @@
17
19
  # ActiveRecordModel.each_row_by_sql("select ...") { |hash| ... }
18
20
  # ActiveRecordModel.each_instance_by_sql("select ...") { |model| ... }
19
21
  #
22
+
23
+
24
+ if ::ActiveRecord::VERSION::MAJOR <= 4
25
+ OID = ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::OID
26
+ else
27
+ OID = ActiveRecord::ConnectionAdapters::PostgreSQL::OID
28
+ end
29
+
20
30
  module PostgreSQLCursor
21
31
  class Cursor
22
32
  include Enumerable
@@ -43,6 +53,7 @@ module PostgreSQLCursor
43
53
  @connection = @options.fetch(:connection) { ::ActiveRecord::Base.connection }
44
54
  @count = 0
45
55
  @iterate = options[:instances] ? :each_instance : :each_row
56
+ @batched = false
46
57
  end
47
58
 
48
59
  # Specify the type to instantiate, or reset to return a Hash
@@ -58,6 +69,11 @@ module PostgreSQLCursor
58
69
  self
59
70
  end
60
71
 
72
+ def iterate_batched(batched=true)
73
+ @batched = batched
74
+ self
75
+ end
76
+
61
77
  # Public: Yields each row of the result set to the passed block
62
78
  #
63
79
  # Yields the row to the block. The row is a hash with symbolized keys.
@@ -66,11 +82,11 @@ module PostgreSQLCursor
66
82
  # Returns the count of rows processed
67
83
  def each(&block)
68
84
  if @iterate == :each_row
69
- self.each_row(&block)
85
+ @batched ? self.each_row_batch(&block) : self.each_row(&block)
70
86
  elsif @iterate == :each_array
71
- self.each_array(&block)
87
+ @batched ? self.each_array_batch(&block) : self.each_array(&block)
72
88
  else
73
- self.each_instance(@type, &block)
89
+ @batched ? self.each_instance_batch(@type, &block) : self.each_instance(@type, &block)
74
90
  end
75
91
  end
76
92
 
@@ -107,6 +123,41 @@ module PostgreSQLCursor
107
123
  end
108
124
  end
109
125
 
126
+ def each_row_batch(&block)
127
+ self.each_batch do |batch|
128
+ batch.map!(&:symbolize_keys) if @options[:symbolize_keys]
129
+ block.call(batch)
130
+ end
131
+ end
132
+
133
+ def each_array_batch(&block)
134
+ old_iterate = @iterate
135
+ @iterate = :each_array
136
+ begin
137
+ rv = self.each_batch do |batch|
138
+ block.call(batch)
139
+ end
140
+ ensure
141
+ @iterate = old_iterate
142
+ end
143
+ rv
144
+ end
145
+
146
+ def each_instance_batch(klass=nil, &block)
147
+ klass ||= @type
148
+ self.each_batch do |batch|
149
+ models = batch.map do |row|
150
+ if ::ActiveRecord::VERSION::MAJOR < 4
151
+ model = klass.send(:instantiate, row)
152
+ else
153
+ @column_types ||= column_types
154
+ model = klass.send(:instantiate, row, @column_types)
155
+ end
156
+ end
157
+ block.call(models)
158
+ end
159
+ end
160
+
110
161
  # Returns an array of columns plucked from the result rows.
111
162
  # Experimental function, as this could still use too much memory
112
163
  # and negate the purpose of this libarary.
@@ -152,6 +203,28 @@ module PostgreSQLCursor
152
203
  @count
153
204
  end
154
205
 
206
+ def each_batch(&block) #:nodoc:
207
+ has_do_until = @options.key?(:until)
208
+ has_do_while = @options.key?(:while)
209
+ @count = 0
210
+ @column_types = nil
211
+ with_optional_transaction do
212
+ begin
213
+ open
214
+ while (batch = fetch_block)
215
+ break if batch.empty?
216
+ @count += 1
217
+ rc = block.call(batch)
218
+ break if has_do_until && rc == @options[:until]
219
+ break if has_do_while && rc != @options[:while]
220
+ end
221
+ ensure
222
+ close if @block
223
+ end
224
+ end
225
+ @count
226
+ end
227
+
155
228
  def cast_types(row)
156
229
  row
157
230
  end
@@ -177,10 +250,9 @@ module PostgreSQLCursor
177
250
  # Public: Opens (actually, "declares") the cursor. Call this before fetching
178
251
  def open
179
252
  set_cursor_tuple_fraction
180
- #@cursor = Digest::MD5.hexdigest(@sql)
181
253
  @cursor = SecureRandom.uuid.gsub("-","")
182
254
  hold = @options[:with_hold] ? 'with hold ' : ''
183
- @result = @connection.execute("declare cursor_#{@cursor} cursor #{hold}for #{@sql}")
255
+ @result = @connection.execute("declare cursor_#{@cursor} no scroll cursor #{hold}for #{@sql}")
184
256
  @block = []
185
257
  end
186
258
 
@@ -1,3 +1,3 @@
1
1
  module PostgresqlCursor
2
- VERSION = "0.6.1"
2
+ VERSION = "0.6.2"
3
3
  end
@@ -1,7 +1,7 @@
1
1
  # A sample Gemfile
2
2
  source "https://rubygems.org"
3
3
 
4
- gem 'activerecord', '~> 3.1.0'
4
+ gem 'activerecord', '~> 5.2.0'
5
5
  #gem 'activerecord', '~> 3.2.0'
6
6
  #gem 'activerecord', '~> 4.0.0'
7
7
  #gem 'activerecord', '~> 4.1.0'
@@ -12,4 +12,4 @@ gem 'activerecord', '~> 3.1.0'
12
12
  #gem 'arel', github: 'rails/arel', branch: 'master'
13
13
 
14
14
  gem 'pg'
15
- gem 'postgresql_cursor', path:"#{ENV['HOME']}/src/postgresql_cursor"
15
+ gem 'postgresql_cursor', path:"../"
@@ -16,6 +16,13 @@ class TestPostgresqlCursor < Minitest::Test
16
16
  assert_equal nn, n
17
17
  end
18
18
 
19
+ def test_each_batch
20
+ c = PostgreSQLCursor::Cursor.new("select * from products order by 1")
21
+ nn = 0
22
+ n = c.each_batch { |b| nn += 1 }
23
+ assert_equal nn, n
24
+ end
25
+
19
26
  def test_enumerables
20
27
  assert_equal true, PostgreSQLCursor::Cursor.new("select * from products order by 1").any?
21
28
  assert_equal false, PostgreSQLCursor::Cursor.new("select * from products where id<0").any?
@@ -23,12 +30,22 @@ class TestPostgresqlCursor < Minitest::Test
23
30
 
24
31
  def test_each_while_until
25
32
  c = PostgreSQLCursor::Cursor.new("select * from products order by 1", until:true)
26
- n = c.each { |r| r[:id].to_i > 100 }
27
- assert_equal 1000, n
33
+ n = c.each { |r| r['id'].to_i > 100 }
34
+ assert_equal 101, n
28
35
 
29
36
  c = PostgreSQLCursor::Cursor.new("select * from products order by 1", while:true)
30
- n = c.each { |r| r[:id].to_i < 100 }
31
- assert_equal 1000, n
37
+ n = c.each { |r| r['id'].to_i < 100 }
38
+ assert_equal 100, n
39
+ end
40
+
41
+ def test_each_batch_while_until
42
+ c = PostgreSQLCursor::Cursor.new("select * from products order by id asc", until: true, block_size: 50)
43
+ n = c.each_batch { |b| b.last['id'].to_i > 100 }
44
+ assert_equal 3, n
45
+
46
+ c = PostgreSQLCursor::Cursor.new("select * from products order by id asc", while: true, block_size: 50)
47
+ n = c.each_batch { |b| b.last['id'].to_i < 100 }
48
+ assert_equal 2, n
32
49
  end
33
50
 
34
51
  def test_each_array
@@ -39,12 +56,36 @@ class TestPostgresqlCursor < Minitest::Test
39
56
  end
40
57
  end
41
58
 
59
+ def test_each_array_batch
60
+ c = PostgreSQLCursor::Cursor.new("select * from products where id = 1")
61
+ c.each_array_batch do |b|
62
+ assert_equal 1, b.size
63
+ ary = b.first
64
+ assert_equal Array, ary.class
65
+ assert_equal 1, ary[0].to_i
66
+ end
67
+ end
68
+
42
69
  def test_relation
43
70
  nn = 0
44
71
  Product.where("id>0").each_row {|r| nn += 1 }
45
72
  assert_equal 1000, nn
46
73
  end
47
74
 
75
+ def test_relation_batch
76
+ nn = 0
77
+ row = nil
78
+ Product.where("id>0").each_row_batch(block_size: 100) { |b| row = b.last; nn += 1 }
79
+ assert_equal 10, nn
80
+ assert_equal Hash, row.class
81
+
82
+ nn = 0
83
+ row = nil
84
+ Product.where("id>0").each_instance_batch(block_size: 100) { |b| row = b.last; nn += 1 }
85
+ assert_equal 10, nn
86
+ assert_equal Product, row.class
87
+ end
88
+
48
89
  def test_activerecord
49
90
  nn = 0
50
91
  row = nil
@@ -58,6 +99,19 @@ class TestPostgresqlCursor < Minitest::Test
58
99
  assert_equal Product, row.class
59
100
  end
60
101
 
102
+ def test_activerecord_batch
103
+ nn = 0
104
+ row = nil
105
+ Product.each_row_batch_by_sql("select * from products", block_size: 100) { |b| row = b.last; nn += 1 }
106
+ assert_equal 10, nn
107
+ assert_equal Hash, row.class
108
+
109
+ nn = 0
110
+ Product.each_instance_batch_by_sql("select * from products", block_size: 100) { |b| row = b.last; nn += 1 }
111
+ assert_equal 10, nn
112
+ assert_equal Product, row.class
113
+ end
114
+
61
115
  def test_exception
62
116
  begin
63
117
  Product.each_row_by_sql("select * from products") do |r|
@@ -68,6 +122,14 @@ class TestPostgresqlCursor < Minitest::Test
68
122
  end
69
123
  end
70
124
 
125
+ def test_batch_exception
126
+ Product.each_row_batch_by_sql("select * from products") do |r|
127
+ raise 'Oops'
128
+ end
129
+ rescue => e
130
+ assert_equal e.message, 'Oops'
131
+ end
132
+
71
133
  def test_cursor
72
134
  cursor = Product.all.each_row
73
135
  assert cursor.respond_to?(:each)
@@ -79,12 +141,23 @@ class TestPostgresqlCursor < Minitest::Test
79
141
  assert_equal 1000, r.size
80
142
  end
81
143
 
144
+ def test_batched_cursor
145
+ cursor = Product.all.each_row_batch(block_size: 100)
146
+ assert cursor.respond_to?(:each)
147
+ b = cursor.map { |batch| batch.map { |r| r['id'] } }
148
+ assert_equal 10, b.size
149
+ cursor = Product.each_row_batch_by_sql("select * from products", block_size: 100)
150
+ assert cursor.respond_to?(:each)
151
+ b = cursor.map { |batch| batch.map { |r| r['id'] } }
152
+ assert_equal 10, b.size
153
+ end
154
+
82
155
  def test_pluck
83
156
  r = Product.pluck_rows(:id)
84
157
  assert_equal 1000, r.size
85
158
  r = Product.all.pluck_instances(:id)
86
159
  assert_equal 1000, r.size
87
- assert_equal Fixnum, r.first.class
160
+ assert_equal Integer, r.first.class
88
161
  end
89
162
 
90
163
  def test_with_hold
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: postgresql_cursor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Allen Fair
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-09-02 00:00:00.000000000 Z
11
+ date: 2018-09-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -116,7 +116,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
116
116
  version: '0'
117
117
  requirements: []
118
118
  rubyforge_project:
119
- rubygems_version: 2.5.1
119
+ rubygems_version: 2.7.7
120
120
  signing_key:
121
121
  specification_version: 4
122
122
  summary: ActiveRecord PostgreSQL Adapter extension for using a cursor to return a