postgresql_cursor 0.6.3 → 0.6.5

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e5f4a26d515960d1540480268329773c23a4e99453216b5bcd54aba599b27938
4
- data.tar.gz: 0fa28ed9b5f9e5a981dda5783135c20c120657edc2ef7077bcfee7eeafc8128e
3
+ metadata.gz: 79f076e2bf123498e0df89e2933531efae406a754cf14d049be33f973fc791c0
4
+ data.tar.gz: 260e4db366a8ccacb370d04c894b643c8363ed666e46fae77c81ec2b79d09b1e
5
5
  SHA512:
6
- metadata.gz: 26ad2f87253cda10a5ebec49b7ed80e44be294460b0a6dea4128a3b6c36e6ad70583921ae1c8cfc89acb00a81e6f744bb0e44c3929adcd97ad360759d2758f8e
7
- data.tar.gz: 2667b5fee47be190f4cd58f068db030fc7ab34087b1ad56449d2b6722e2205baceae18142ae4692241b1c92cc9382076446283b5807fa9234e70af7799489dba
6
+ metadata.gz: 18720db87b3218629b6658a7fec653f56775eed3cea0af4cae6d0422a93b7f2dfa338c47752f5be84f46a1b72dbee4c0ec677ee7e6057894ad85c31937fef02b
7
+ data.tar.gz: 48aff84018a7924fff8c1eefbef1240b5adec9f43c12d29fd744b8af80ef283edae66249f5546d7c81741fed93d70838ac494031518a2814c4f44e43f5ff3944
data/.gitignore CHANGED
@@ -15,6 +15,7 @@ tmtags
15
15
 
16
16
  ## IntelliJ/Rubymine
17
17
  .idea
18
+ *.iml
18
19
 
19
20
  ## PROJECT::GENERAL
20
21
  coverage
@@ -22,4 +23,4 @@ rdoc
22
23
  pkg
23
24
 
24
25
  ## PROJECT::SPECIFIC
25
- Gemfile.lock
26
+ Gemfile.lock
data/.travis.yml ADDED
@@ -0,0 +1,27 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.6.5
4
+ - 2.7.1
5
+ before_install:
6
+ - sudo apt-get update
7
+ - sudo apt-get --yes remove postgresql\*
8
+ - sudo apt-get install -y postgresql-12 postgresql-client-12
9
+ - sudo cp /etc/postgresql/{9.6,12}/main/pg_hba.conf
10
+ - sudo service postgresql restart 12
11
+ gemfile:
12
+ - gemfiles/activerecord_4.gemfile
13
+ - gemfiles/activerecord_5.gemfile
14
+ - gemfiles/activerecord_6.gemfile
15
+ matrix:
16
+ exclude:
17
+ - rvm: 2.7.1
18
+ gemfile: gemfiles/activerecord_4.gemfile
19
+ services:
20
+ - postgresql
21
+ before_script:
22
+ - psql -c 'create database postgresql_cursor_test;' -U postgres
23
+ - psql -c 'CREATE ROLE travis SUPERUSER LOGIN CREATEDB;' -U postgres
24
+ - psql -c 'create table products ( id serial primary key, data varchar);' -U postgres -d postgresql_cursor_test
25
+ - psql -c 'create table prices ( id serial primary key, data varchar, product_id integer);' -U postgres -d postgresql_cursor_test
26
+ addons:
27
+ postgresql: '12.3'
data/Appraisals ADDED
@@ -0,0 +1,12 @@
1
+ appraise "activerecord-4" do
2
+ gem "activerecord", "4.2.11.1"
3
+ gem "pg", "~> 0.15"
4
+ end
5
+
6
+ appraise "activerecord-5" do
7
+ gem "activerecord", "5.2.3"
8
+ end
9
+
10
+ appraise "activerecord-6" do
11
+ gem "activerecord", "6.0.0"
12
+ end
data/Rakefile CHANGED
@@ -11,7 +11,7 @@ end
11
11
 
12
12
  desc "Open and IRB Console with the gem and test-app loaded"
13
13
  task :console do
14
- sh "bundle exec irb -Ilib -I . -r postgresql_cursor -r test-app/app"
14
+ sh "bundle exec irb -Ilib -I . -r pg -r postgresql_cursor -r test-app/app"
15
15
  #require 'irb'
16
16
  #ARGV.clear
17
17
  #IRB.start
@@ -21,4 +21,5 @@ desc "Setup testing database and table"
21
21
  task :setup do
22
22
  sh %q(createdb postgresql_cursor_test)
23
23
  sh %Q<echo "create table products ( id serial primary key, data varchar);" | psql postgresql_cursor_test>
24
+ sh %Q<echo "create table prices ( id serial primary key, data varchar, product_id integer);" | psql postgresql_cursor_test>
24
25
  end
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "4.2.11.1"
6
+ gem "pg", "~> 0.15"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "5.2.3"
6
+
7
+ gemspec path: "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "6.0.0"
6
+
7
+ gemspec path: "../"
@@ -41,29 +41,32 @@ module PostgreSQLCursor
41
41
  # PostgreSQLCursor::Cursor.new("select ....")
42
42
  #
43
43
  # Returns the cursor object when called with new.
44
- def initialize(sql, options={})
45
- @sql = sql
46
- @options = options
44
+ def initialize(sql, options = {})
45
+ @sql = sql
46
+ @options = options
47
47
  @connection = @options.fetch(:connection) { ::ActiveRecord::Base.connection }
48
- @count = 0
49
- @iterate = options[:instances] ? :each_instance : :each_row
50
- @batched = false
48
+ @count = 0
49
+ @iterate = options[:instances] ? :each_instance : :each_row
50
+ @batched = false
51
51
  end
52
52
 
53
- # Specify the type to instantiate, or reset to return a Hash
54
- def iterate_type(type=nil)
55
- if type.nil? || type == Hash
53
+ # Specify the type to instantiate, or reset to return a Hash.
54
+ #
55
+ # Explicitly check for type class to prevent calling equality
56
+ # operator on active record relation, which will load it.
57
+ def iterate_type(type = nil)
58
+ if type.nil? || (type.instance_of?(Class) && type == Hash)
56
59
  @iterate = :each_row
57
- elsif type == Array
60
+ elsif type.instance_of?(Class) && type == Array
58
61
  @iterate = :each_array
59
62
  else
60
63
  @iterate = :each_instance
61
- @type = type
64
+ @type = type
62
65
  end
63
66
  self
64
67
  end
65
68
 
66
- def iterate_batched(batched=true)
69
+ def iterate_batched(batched = true)
67
70
  @batched = batched
68
71
  self
69
72
  end
@@ -76,16 +79,16 @@ module PostgreSQLCursor
76
79
  # Returns the count of rows processed
77
80
  def each(&block)
78
81
  if @iterate == :each_row
79
- @batched ? self.each_row_batch(&block) : self.each_row(&block)
82
+ @batched ? each_row_batch(&block) : each_row(&block)
80
83
  elsif @iterate == :each_array
81
- @batched ? self.each_array_batch(&block) : self.each_array(&block)
84
+ @batched ? each_array_batch(&block) : each_array(&block)
82
85
  else
83
- @batched ? self.each_instance_batch(@type, &block) : self.each_instance(@type, &block)
86
+ @batched ? each_instance_batch(@type, &block) : each_instance(@type, &block)
84
87
  end
85
88
  end
86
89
 
87
90
  def each_row(&block)
88
- self.each_tuple do |row|
91
+ each_tuple do |row|
89
92
  row = row.symbolize_keys if @options[:symbolize_keys]
90
93
  block.call(row)
91
94
  end
@@ -95,7 +98,7 @@ module PostgreSQLCursor
95
98
  old_iterate = @iterate
96
99
  @iterate = :each_array
97
100
  begin
98
- rv = self.each_tuple do |row|
101
+ rv = each_tuple do |row|
99
102
  block.call(row)
100
103
  end
101
104
  ensure
@@ -104,11 +107,11 @@ module PostgreSQLCursor
104
107
  rv
105
108
  end
106
109
 
107
- def each_instance(klass=nil, &block)
110
+ def each_instance(klass = nil, &block)
108
111
  klass ||= @type
109
- self.each_tuple do |row|
112
+ each_tuple do |row|
110
113
  if ::ActiveRecord::VERSION::MAJOR < 4
111
- model = klass.send(:instantiate,row)
114
+ model = klass.send(:instantiate, row)
112
115
  else
113
116
  @column_types ||= column_types
114
117
  model = klass.send(:instantiate, row, @column_types)
@@ -118,7 +121,7 @@ module PostgreSQLCursor
118
121
  end
119
122
 
120
123
  def each_row_batch(&block)
121
- self.each_batch do |batch|
124
+ each_batch do |batch|
122
125
  batch.map!(&:symbolize_keys) if @options[:symbolize_keys]
123
126
  block.call(batch)
124
127
  end
@@ -128,7 +131,7 @@ module PostgreSQLCursor
128
131
  old_iterate = @iterate
129
132
  @iterate = :each_array
130
133
  begin
131
- rv = self.each_batch do |batch|
134
+ rv = each_batch do |batch|
132
135
  block.call(batch)
133
136
  end
134
137
  ensure
@@ -137,15 +140,15 @@ module PostgreSQLCursor
137
140
  rv
138
141
  end
139
142
 
140
- def each_instance_batch(klass=nil, &block)
143
+ def each_instance_batch(klass = nil, &block)
141
144
  klass ||= @type
142
- self.each_batch do |batch|
145
+ each_batch do |batch|
143
146
  models = batch.map do |row|
144
147
  if ::ActiveRecord::VERSION::MAJOR < 4
145
- model = klass.send(:instantiate, row)
148
+ klass.send(:instantiate, row)
146
149
  else
147
150
  @column_types ||= column_types
148
- model = klass.send(:instantiate, row, @column_types)
151
+ klass.send(:instantiate, row, @column_types)
149
152
  end
150
153
  end
151
154
  block.call(models)
@@ -160,11 +163,11 @@ module PostgreSQLCursor
160
163
  options = cols.last.is_a?(Hash) ? cols.pop : {}
161
164
  @options.merge!(options)
162
165
  @options[:symbolize_keys] = true
163
- self.iterate_type(options[:class]) if options[:class]
164
- cols = cols.map {|c| c.to_sym }
165
- result = []
166
+ iterate_type(options[:class]) if options[:class]
167
+ cols = cols.map { |c| c.to_sym }
168
+ result = []
166
169
 
167
- self.each() do |row|
170
+ each do |row|
168
171
  row = row.symbolize_keys if row.is_a?(Hash)
169
172
  result << cols.map { |c| row[c] }
170
173
  end
@@ -173,48 +176,44 @@ module PostgreSQLCursor
173
176
  result
174
177
  end
175
178
 
176
- def each_tuple(&block) #:nodoc:
177
- has_do_until = @options.has_key?(:until)
178
- has_do_while = @options.has_key?(:while)
179
- @count = 0
179
+ def each_tuple(&block) # :nodoc:
180
+ has_do_until = @options.has_key?(:until)
181
+ has_do_while = @options.has_key?(:while)
182
+ @count = 0
180
183
  @column_types = nil
181
184
  with_optional_transaction do
182
- begin
183
- open
184
- while (row = fetch) do
185
- break if row.size==0
186
- @count += 1
187
- rc = block.call(row)
188
- break if has_do_until && rc == @options[:until]
189
- break if has_do_while && rc != @options[:while]
190
- end
191
- rescue Exception => e
192
- raise e
193
- ensure
194
- close if @block
185
+ open
186
+ while (row = fetch)
187
+ break if row.size == 0
188
+ @count += 1
189
+ rc = block.call(row)
190
+ break if has_do_until && rc == @options[:until]
191
+ break if has_do_while && rc != @options[:while]
195
192
  end
193
+ rescue => e
194
+ raise e
195
+ ensure
196
+ close if @block && connection.active?
196
197
  end
197
198
  @count
198
199
  end
199
200
 
200
- def each_batch(&block) #:nodoc:
201
+ def each_batch(&block) # :nodoc:
201
202
  has_do_until = @options.key?(:until)
202
203
  has_do_while = @options.key?(:while)
203
204
  @count = 0
204
205
  @column_types = nil
205
206
  with_optional_transaction do
206
- begin
207
- open
208
- while (batch = fetch_block)
209
- break if batch.empty?
210
- @count += 1
211
- rc = block.call(batch)
212
- break if has_do_until && rc == @options[:until]
213
- break if has_do_while && rc != @options[:while]
214
- end
215
- ensure
216
- close if @block
207
+ open
208
+ while (batch = fetch_block)
209
+ break if batch.empty?
210
+ @count += 1
211
+ rc = block.call(batch)
212
+ break if has_do_until && rc == @options[:until]
213
+ break if has_do_while && rc != @options[:while]
217
214
  end
215
+ ensure
216
+ close if @block && connection.active?
218
217
  end
219
218
  @count
220
219
  end
@@ -231,15 +230,15 @@ module PostgreSQLCursor
231
230
  fields = @result.fields
232
231
  fields.each_with_index do |fname, i|
233
232
  ftype = @result.ftype i
234
- fmod = @result.fmod i
235
- types[fname] = @connection.get_type_map.fetch(ftype, fmod) { |oid, mod|
233
+ fmod = @result.fmod i
234
+ types[fname] = @connection.get_type_map.fetch(ftype, fmod) do |oid, mod|
236
235
  warn "unknown OID: #{fname}(#{oid}) (#{sql})"
237
236
  if ::ActiveRecord::VERSION::MAJOR <= 4
238
- ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::OID::Identity.new
237
+ ::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::OID::Identity.new
239
238
  else
240
- ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Identity.new
239
+ ::ActiveRecord::Type::Value.new
241
240
  end
242
- }
241
+ end
243
242
  end
244
243
 
245
244
  @column_types = types
@@ -248,8 +247,8 @@ module PostgreSQLCursor
248
247
  # Public: Opens (actually, "declares") the cursor. Call this before fetching
249
248
  def open
250
249
  set_cursor_tuple_fraction
251
- @cursor = @options[:cursor_name] || ("cursor_" + SecureRandom.uuid.gsub("-",""))
252
- hold = @options[:with_hold] ? 'with hold ' : ''
250
+ @cursor = @options[:cursor_name] || ("cursor_" + SecureRandom.uuid.delete("-"))
251
+ hold = @options[:with_hold] ? "with hold " : ""
253
252
  @result = @connection.execute("declare #{@cursor} no scroll cursor #{hold}for #{@sql}")
254
253
  @block = []
255
254
  end
@@ -257,23 +256,23 @@ module PostgreSQLCursor
257
256
  # Public: Returns the next row from the cursor, or empty hash if end of results
258
257
  #
259
258
  # Returns a row as a hash of {'colname'=>value,...}
260
- def fetch(options={})
259
+ def fetch(options = {})
261
260
  open unless @block
262
- fetch_block if @block.size==0
261
+ fetch_block if @block.size == 0
263
262
  row = @block.shift
264
263
  row = row.symbolize_keys if row && options[:symbolize_keys]
265
264
  row
266
265
  end
267
266
 
268
267
  # Private: Fetches the next block of rows into @block
269
- def fetch_block(block_size=nil)
270
- block_size ||= @block_size ||= @options.fetch(:block_size) { 1000 }
268
+ def fetch_block(block_size = nil)
269
+ block_size ||= @block_size ||= @options.fetch(:block_size, 1000)
271
270
  @result = @connection.execute("fetch #{block_size} from #{@cursor}")
272
271
 
273
- if @iterate == :each_array
274
- @block = @result.each_row.collect {|row| row }
272
+ @block = if @iterate == :each_array
273
+ @result.each_row.collect { |row| row }
275
274
  else
276
- @block = @result.collect {|row| row }
275
+ @result.collect { |row| row }
277
276
  end
278
277
  end
279
278
 
@@ -295,8 +294,8 @@ module PostgreSQLCursor
295
294
  # This is a value between 0.1 and 1.0 (PostgreSQL defaults to 0.1, this library defaults to 1.0)
296
295
  # used to determine the expected fraction (percent) of result rows returned the the caller.
297
296
  # This value determines the access path by the query planner.
298
- def set_cursor_tuple_fraction(frac=1.0)
299
- @cursor_tuple_fraction ||= @options.fetch(:fraction) { 1.0 }
297
+ def set_cursor_tuple_fraction(frac = 1.0)
298
+ @cursor_tuple_fraction ||= @options.fetch(:fraction, 1.0)
300
299
  return @cursor_tuple_fraction if frac == @cursor_tuple_fraction
301
300
  @cursor_tuple_fraction = frac
302
301
  @result = @connection.execute("set cursor_tuple_fraction to #{frac}")
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PostgresqlCursor
2
- VERSION = "0.6.3"
4
+ VERSION = "0.6.5"
3
5
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'postgresql_cursor/version'
4
+ require 'active_support'
4
5
 
5
6
  ActiveSupport.on_load :active_record do
6
7
  require 'postgresql_cursor/cursor'
@@ -1,32 +1,44 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path("lib", __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'postgresql_cursor/version'
5
+ require "postgresql_cursor/version"
5
6
 
6
7
  Gem::Specification.new do |spec|
7
- spec.name = "postgresql_cursor"
8
- spec.version = PostgresqlCursor::VERSION
9
- spec.authors = ["Allen Fair"]
10
- spec.email = ["allen.fair@gmail.com"]
11
- spec.summary = "ActiveRecord PostgreSQL Adapter extension for using a cursor to return a large result set"
12
- spec.description = "PostgreSQL Cursor is an extension to the ActiveRecord PostgreSQLAdapter for very large result sets. It provides a cursor open/fetch/close interface to access data without loading all rows into memory, and instead loads the result rows in \"chunks\" (default of 1_000 rows), buffers them, and returns the rows one at a time."
13
- spec.homepage = "http://github.com/afair/postgresql_cursor"
14
- spec.license = "MIT"
8
+ spec.name = "postgresql_cursor"
9
+ spec.version = PostgresqlCursor::VERSION
10
+ spec.authors = ["Allen Fair"]
11
+ spec.email = ["allen.fair@gmail.com"]
12
+ spec.summary = <<-SUMMARY
13
+ ActiveRecord PostgreSQL Adapter extension for using a cursor to return a
14
+ large result set
15
+ SUMMARY
16
+ spec.description = <<-DESCRIPTION
17
+ PostgreSQL Cursor is an extension to the ActiveRecord PostgreSQLAdapter for
18
+ very large result sets. It provides a cursor open/fetch/close interface to
19
+ access data without loading all rows into memory, and instead loads the result
20
+ rows in 'chunks' (default of 1_000 rows), buffers them, and returns the rows
21
+ one at a time.
22
+ DESCRIPTION
23
+ spec.homepage = "http://github.com/afair/postgresql_cursor"
24
+ spec.license = "MIT"
15
25
 
16
- spec.files = `git ls-files -z`.split("\x0")
17
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
26
+ spec.files = `git ls-files -z`.split("\x0")
27
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
28
  spec.require_paths = ["lib"]
20
29
 
21
- #spec.add_dependency "pg" # Remove this for jruby, which should specify 'activerecord-jdbcpostgresql-adapter'
22
- spec.add_dependency "activerecord", ">= 3.1.0"
23
- #spec.add_dependency "activerecord", "~> 3.1.0"
24
- # Tests don't run on 4.0.0 since AR/AS have an older version of minitest as a run-time dependency(!) than our tests support
25
- #spec.add_dependency "activerecord", "~> 4.0.0";# spec.add_dependency "minitest", "~> 4.2.0"
26
- #spec.add_dependency "activerecord", "~> 4.1.0"
27
- #spec.add_dependency "activerecord", "~> 5.0.0.beta2"
30
+ # Remove this for jruby which should use 'activerecord-jdbcpostgresql-adapter'
31
+ # spec.add_dependency 'pg'
32
+
33
+ spec.add_dependency "activerecord", ">= 7.0.0"
34
+ # spec.add_dependency 'activerecord', '~> 3.1.0'
35
+ # spec.add_dependency 'activerecord', '~> 4.1.0'
36
+ # spec.add_dependency 'activerecord', '~> 5.0.0'
37
+ # spec.add_dependency 'activerecord', '~> 6.0.0'
28
38
 
39
+ spec.add_development_dependency "irb"
40
+ spec.add_development_dependency "minitest"
29
41
  spec.add_development_dependency "pg"
30
42
  spec.add_development_dependency "rake"
31
- spec.add_development_dependency "minitest"
43
+ spec.add_development_dependency "appraisal"
32
44
  end
data/test/helper.rb CHANGED
@@ -10,6 +10,8 @@ ActiveRecord::Base.establish_connection(adapter: 'postgresql',
10
10
  username: ENV['TEST_USER'] || ENV['USER'] || 'postgresql_cursor')
11
11
 
12
12
  class Product < ActiveRecord::Base
13
+ has_many :prices
14
+
13
15
  # create table records (id serial primary key);
14
16
  def self.generate(max=1_000)
15
17
  max.times do |i|
@@ -18,5 +20,9 @@ class Product < ActiveRecord::Base
18
20
  end
19
21
  end
20
22
 
23
+ class Price < ActiveRecord::Base
24
+ belongs_to :product
25
+ end
26
+
21
27
  Product.destroy_all
22
28
  Product.generate(1000)
@@ -3,16 +3,15 @@
3
3
  # rake setup
4
4
  # or create the database manually if your environment doesn't permit
5
5
  ################################################################################
6
- require_relative 'helper'
7
- require 'minitest/autorun'
8
- require 'minitest/pride'
6
+ require_relative "helper"
7
+ require "minitest/autorun"
8
+ require "minitest/pride"
9
9
 
10
10
  class TestPostgresqlCursor < Minitest::Test
11
-
12
11
  def test_each
13
12
  c = PostgreSQLCursor::Cursor.new("select * from products order by 1")
14
13
  nn = 0
15
- n = c.each { nn += 1}
14
+ n = c.each { nn += 1 }
16
15
  assert_equal nn, n
17
16
  end
18
17
 
@@ -29,22 +28,22 @@ class TestPostgresqlCursor < Minitest::Test
29
28
  end
30
29
 
31
30
  def test_each_while_until
32
- c = PostgreSQLCursor::Cursor.new("select * from products order by 1", until:true)
33
- n = c.each { |r| r['id'].to_i > 100 }
31
+ c = PostgreSQLCursor::Cursor.new("select * from products order by 1", until: true)
32
+ n = c.each { |r| r["id"].to_i > 100 }
34
33
  assert_equal 101, n
35
34
 
36
- c = PostgreSQLCursor::Cursor.new("select * from products order by 1", while:true)
37
- n = c.each { |r| r['id'].to_i < 100 }
35
+ c = PostgreSQLCursor::Cursor.new("select * from products order by 1", while: true)
36
+ n = c.each { |r| r["id"].to_i < 100 }
38
37
  assert_equal 100, n
39
38
  end
40
39
 
41
40
  def test_each_batch_while_until
42
41
  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 }
42
+ n = c.each_batch { |b| b.last["id"].to_i > 100 }
44
43
  assert_equal 3, n
45
44
 
46
45
  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 }
46
+ n = c.each_batch { |b| b.last["id"].to_i < 100 }
48
47
  assert_equal 2, n
49
48
  end
50
49
 
@@ -68,20 +67,26 @@ class TestPostgresqlCursor < Minitest::Test
68
67
 
69
68
  def test_relation
70
69
  nn = 0
71
- Product.where("id>0").each_row {|r| nn += 1 }
70
+ Product.where("id>0").each_row { |r| nn += 1 }
72
71
  assert_equal 1000, nn
73
72
  end
74
73
 
75
74
  def test_relation_batch
76
75
  nn = 0
77
76
  row = nil
78
- Product.where("id>0").each_row_batch(block_size: 100) { |b| row = b.last; nn += 1 }
77
+ Product.where("id>0").each_row_batch(block_size: 100) { |b|
78
+ row = b.last
79
+ nn += 1
80
+ }
79
81
  assert_equal 10, nn
80
82
  assert_equal Hash, row.class
81
83
 
82
84
  nn = 0
83
85
  row = nil
84
- Product.where("id>0").each_instance_batch(block_size: 100) { |b| row = b.last; nn += 1 }
86
+ Product.where("id>0").each_instance_batch(block_size: 100) { |b|
87
+ row = b.last
88
+ nn += 1
89
+ }
85
90
  assert_equal 10, nn
86
91
  assert_equal Product, row.class
87
92
  end
@@ -89,12 +94,18 @@ class TestPostgresqlCursor < Minitest::Test
89
94
  def test_activerecord
90
95
  nn = 0
91
96
  row = nil
92
- Product.each_row_by_sql("select * from products") {|r| row = r; nn += 1 }
97
+ Product.each_row_by_sql("select * from products") { |r|
98
+ row = r
99
+ nn += 1
100
+ }
93
101
  assert_equal 1000, nn
94
102
  assert_equal Hash, row.class
95
103
 
96
104
  nn = 0
97
- Product.each_instance_by_sql("select * from products") {|r| row = r; nn += 1 }
105
+ Product.each_instance_by_sql("select * from products") { |r|
106
+ row = r
107
+ nn += 1
108
+ }
98
109
  assert_equal 1000, nn
99
110
  assert_equal Product, row.class
100
111
  end
@@ -102,32 +113,52 @@ class TestPostgresqlCursor < Minitest::Test
102
113
  def test_activerecord_batch
103
114
  nn = 0
104
115
  row = nil
105
- Product.each_row_batch_by_sql("select * from products", block_size: 100) { |b| row = b.last; nn += 1 }
116
+ Product.each_row_batch_by_sql("select * from products", block_size: 100) { |b|
117
+ row = b.last
118
+ nn += 1
119
+ }
106
120
  assert_equal 10, nn
107
121
  assert_equal Hash, row.class
108
122
 
109
123
  nn = 0
110
- Product.each_instance_batch_by_sql("select * from products", block_size: 100) { |b| row = b.last; nn += 1 }
124
+ Product.each_instance_batch_by_sql("select * from products", block_size: 100) { |b|
125
+ row = b.last
126
+ nn += 1
127
+ }
111
128
  assert_equal 10, nn
112
129
  assert_equal Product, row.class
113
130
  end
114
131
 
115
132
  def test_exception
116
- begin
117
- Product.each_row_by_sql("select * from products") do |r|
118
- raise "Oops"
119
- end
120
- rescue Exception => e
121
- assert_equal e.message, 'Oops'
133
+ Product.each_row_by_sql("select * from products") do |r|
134
+ raise "Oops"
122
135
  end
136
+ rescue => e
137
+ assert_equal e.message, "Oops"
123
138
  end
124
139
 
125
140
  def test_batch_exception
126
141
  Product.each_row_batch_by_sql("select * from products") do |r|
127
- raise 'Oops'
142
+ raise "Oops"
143
+ end
144
+ rescue => e
145
+ assert_equal e.message, "Oops"
146
+ end
147
+
148
+ def test_exception_in_failed_transaction
149
+ Product.each_row_by_sql("select * from products") do |r|
150
+ Product.connection.execute("select kaboom")
151
+ end
152
+ rescue => e
153
+ assert_match(/PG::InFailedSqlTransaction/, e.message)
154
+ end
155
+
156
+ def test_batch_exception_in_failed_transaction
157
+ Product.each_row_batch_by_sql("select * from products") do |r|
158
+ Product.connection.execute("select kaboom")
128
159
  end
129
160
  rescue => e
130
- assert_equal e.message, 'Oops'
161
+ assert_match(/PG::InFailedSqlTransaction/, e.message)
131
162
  end
132
163
 
133
164
  def test_cursor
@@ -144,11 +175,11 @@ class TestPostgresqlCursor < Minitest::Test
144
175
  def test_batched_cursor
145
176
  cursor = Product.all.each_row_batch(block_size: 100)
146
177
  assert cursor.respond_to?(:each)
147
- b = cursor.map { |batch| batch.map { |r| r['id'] } }
178
+ b = cursor.map { |batch| batch.map { |r| r["id"] } }
148
179
  assert_equal 10, b.size
149
180
  cursor = Product.each_row_batch_by_sql("select * from products", block_size: 100)
150
181
  assert cursor.respond_to?(:each)
151
- b = cursor.map { |batch| batch.map { |r| r['id'] } }
182
+ b = cursor.map { |batch| batch.map { |r| r["id"] } }
152
183
  assert_equal 10, b.size
153
184
  end
154
185
 
@@ -162,9 +193,9 @@ class TestPostgresqlCursor < Minitest::Test
162
193
 
163
194
  def test_with_hold
164
195
  items = 0
165
- Product.where("id < 4") .each_instance(with_hold: true, block_size:1) do |row|
196
+ Product.where("id < 4").each_instance(with_hold: true, block_size: 1) do |row|
166
197
  Product.transaction do
167
- row.update(data:Time.now.to_f.to_s)
198
+ row.update(data: Time.now.to_f.to_s)
168
199
  items += 1
169
200
  end
170
201
  end
@@ -173,22 +204,25 @@ class TestPostgresqlCursor < Minitest::Test
173
204
 
174
205
  def test_fetch_symbolize_keys
175
206
  Product.transaction do
176
- #cursor = PostgreSQLCursor::Cursor.new("select * from products order by 1")
207
+ # cursor = PostgreSQLCursor::Cursor.new("select * from products order by 1")
177
208
  cursor = Product.all.each_row
178
209
  r = cursor.fetch
179
210
  assert r.has_key?("id")
180
- r = cursor.fetch(symbolize_keys:true)
211
+ r = cursor.fetch(symbolize_keys: true)
181
212
  assert r.has_key?(:id)
182
213
  cursor.close
183
214
  end
184
215
  end
185
216
 
186
217
  def test_bad_sql
187
- begin
188
- ActiveRecord::Base.each_row_by_sql('select * from bad_table') { }
189
- raise "Did Not Raise Expected Exception"
190
- rescue Exception => e
191
- assert_match(/bad_table/, e.message)
192
- end
218
+ ActiveRecord::Base.each_row_by_sql("select * from bad_table") {}
219
+ raise "Did Not Raise Expected Exception"
220
+ rescue => e
221
+ assert_match(/bad_table/, e.message)
222
+ end
223
+
224
+ def test_relation_association_is_not_loaded
225
+ cursor = Product.first.prices.each_instance
226
+ refute cursor.instance_variable_get(:@type).loaded?
193
227
  end
194
228
  end
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.3
4
+ version: 0.6.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Allen Fair
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-09-30 00:00:00.000000000 Z
11
+ date: 2022-10-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -16,14 +16,42 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 3.1.0
19
+ version: 7.0.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 3.1.0
26
+ version: 7.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: irb
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
27
55
  - !ruby/object:Gem::Dependency
28
56
  name: pg
29
57
  requirement: !ruby/object:Gem::Requirement
@@ -53,7 +81,7 @@ dependencies:
53
81
  - !ruby/object:Gem::Version
54
82
  version: '0'
55
83
  - !ruby/object:Gem::Dependency
56
- name: minitest
84
+ name: appraisal
57
85
  requirement: !ruby/object:Gem::Requirement
58
86
  requirements:
59
87
  - - ">="
@@ -66,10 +94,12 @@ dependencies:
66
94
  - - ">="
67
95
  - !ruby/object:Gem::Version
68
96
  version: '0'
69
- description: PostgreSQL Cursor is an extension to the ActiveRecord PostgreSQLAdapter
70
- for very large result sets. It provides a cursor open/fetch/close interface to access
71
- data without loading all rows into memory, and instead loads the result rows in
72
- "chunks" (default of 1_000 rows), buffers them, and returns the rows one at a time.
97
+ description: |2
98
+ PostgreSQL Cursor is an extension to the ActiveRecord PostgreSQLAdapter for
99
+ very large result sets. It provides a cursor open/fetch/close interface to
100
+ access data without loading all rows into memory, and instead loads the result
101
+ rows in 'chunks' (default of 1_000 rows), buffers them, and returns the rows
102
+ one at a time.
73
103
  email:
74
104
  - allen.fair@gmail.com
75
105
  executables: []
@@ -78,11 +108,15 @@ extra_rdoc_files: []
78
108
  files:
79
109
  - ".document"
80
110
  - ".gitignore"
111
+ - ".travis.yml"
112
+ - Appraisals
81
113
  - Gemfile
82
- - Gemfile.lock
83
114
  - LICENSE
84
115
  - README.md
85
116
  - Rakefile
117
+ - gemfiles/activerecord_4.gemfile
118
+ - gemfiles/activerecord_5.gemfile
119
+ - gemfiles/activerecord_6.gemfile
86
120
  - lib/postgresql_cursor.rb
87
121
  - lib/postgresql_cursor/active_record/connection_adapters/postgresql_type_map.rb
88
122
  - lib/postgresql_cursor/active_record/relation/cursor_iterators.rb
@@ -100,7 +134,7 @@ homepage: http://github.com/afair/postgresql_cursor
100
134
  licenses:
101
135
  - MIT
102
136
  metadata: {}
103
- post_install_message:
137
+ post_install_message:
104
138
  rdoc_options: []
105
139
  require_paths:
106
140
  - lib
@@ -115,11 +149,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
115
149
  - !ruby/object:Gem::Version
116
150
  version: '0'
117
151
  requirements: []
118
- rubygems_version: 3.0.6
119
- signing_key:
152
+ rubygems_version: 3.3.7
153
+ signing_key:
120
154
  specification_version: 4
121
155
  summary: ActiveRecord PostgreSQL Adapter extension for using a cursor to return a
122
156
  large result set
123
- test_files:
124
- - test/helper.rb
125
- - test/test_postgresql_cursor.rb
157
+ test_files: []
data/Gemfile.lock DELETED
@@ -1,42 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- postgresql_cursor (0.6.3)
5
- activerecord (>= 3.1.0)
6
-
7
- GEM
8
- remote: https://rubygems.org/
9
- specs:
10
- activemodel (6.0.0)
11
- activesupport (= 6.0.0)
12
- activerecord (6.0.0)
13
- activemodel (= 6.0.0)
14
- activesupport (= 6.0.0)
15
- activesupport (6.0.0)
16
- concurrent-ruby (~> 1.0, >= 1.0.2)
17
- i18n (>= 0.7, < 2)
18
- minitest (~> 5.1)
19
- tzinfo (~> 1.1)
20
- zeitwerk (~> 2.1, >= 2.1.8)
21
- concurrent-ruby (1.1.5)
22
- i18n (1.6.0)
23
- concurrent-ruby (~> 1.0)
24
- minitest (5.12.0)
25
- pg (1.1.4)
26
- rake (13.0.0)
27
- thread_safe (0.3.6)
28
- tzinfo (1.2.5)
29
- thread_safe (~> 0.1)
30
- zeitwerk (2.1.10)
31
-
32
- PLATFORMS
33
- ruby
34
-
35
- DEPENDENCIES
36
- minitest
37
- pg
38
- postgresql_cursor!
39
- rake
40
-
41
- BUNDLED WITH
42
- 1.17.3