postgresql_cursor 0.5.1 → 0.6.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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Gemfile.lock +21 -18
- data/README.md +39 -4
- data/Rakefile +1 -1
- data/lib/postgresql_cursor/active_record/sql_cursor.rb +2 -0
- data/lib/postgresql_cursor/cursor.rb +10 -4
- data/lib/postgresql_cursor/version.rb +1 -1
- data/postgresql_cursor.gemspec +7 -2
- data/test-app/.gitignore +1 -0
- data/test-app/Gemfile +2 -1
- data/test/helper.rb +1 -1
- data/test/test_postgresql_cursor.rb +32 -1
- metadata +6 -7
- data/VERSION +0 -1
- data/test-app/Gemfile.lock +0 -34
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8bd16d1710bfc6796d8410b99d01b17c085118df
|
4
|
+
data.tar.gz: 4ad5f707f0c96ddbf85fb57ed2935d7c6e2e1da6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 69547256fbc79ab776b41a05729ead0eb05bc0c6cc94fb2da60a19922376e4ddbd3fa686f76f3ed0bd2e98e35e16a072828fdae0f29f687fc4e14ff0364bd8be
|
7
|
+
data.tar.gz: 90e5cda34d77577766143c53b26300696791298ac2c9f20ba09d204e9be443ccafa3c0bbbf70619679e829abaf2734ef271f59991352739dfcab54c63e6050f1
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,33 +1,33 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
postgresql_cursor (0.
|
5
|
-
activerecord (>= 3.
|
4
|
+
postgresql_cursor (0.6.0)
|
5
|
+
activerecord (>= 3.1.0)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
|
-
activemodel (4.
|
11
|
-
activesupport (= 4.
|
10
|
+
activemodel (4.2.5.1)
|
11
|
+
activesupport (= 4.2.5.1)
|
12
12
|
builder (~> 3.1)
|
13
|
-
activerecord (4.
|
14
|
-
activemodel (= 4.
|
15
|
-
activesupport (= 4.
|
16
|
-
arel (~>
|
17
|
-
activesupport (4.
|
18
|
-
i18n (~> 0.
|
13
|
+
activerecord (4.2.5.1)
|
14
|
+
activemodel (= 4.2.5.1)
|
15
|
+
activesupport (= 4.2.5.1)
|
16
|
+
arel (~> 6.0)
|
17
|
+
activesupport (4.2.5.1)
|
18
|
+
i18n (~> 0.7)
|
19
19
|
json (~> 1.7, >= 1.7.7)
|
20
20
|
minitest (~> 5.1)
|
21
|
-
thread_safe (~> 0.
|
21
|
+
thread_safe (~> 0.3, >= 0.3.4)
|
22
22
|
tzinfo (~> 1.1)
|
23
|
-
arel (
|
23
|
+
arel (6.0.3)
|
24
24
|
builder (3.2.2)
|
25
|
-
i18n (0.
|
26
|
-
json (1.8.
|
27
|
-
minitest (5.
|
28
|
-
pg (0.
|
29
|
-
rake (10.
|
30
|
-
thread_safe (0.3.
|
25
|
+
i18n (0.7.0)
|
26
|
+
json (1.8.3)
|
27
|
+
minitest (5.8.4)
|
28
|
+
pg (0.18.4)
|
29
|
+
rake (10.5.0)
|
30
|
+
thread_safe (0.3.5)
|
31
31
|
tzinfo (1.2.2)
|
32
32
|
thread_safe (~> 0.1)
|
33
33
|
|
@@ -39,3 +39,6 @@ DEPENDENCIES
|
|
39
39
|
pg
|
40
40
|
postgresql_cursor!
|
41
41
|
rake
|
42
|
+
|
43
|
+
BUNDLED WITH
|
44
|
+
1.11.2
|
data/README.md
CHANGED
@@ -13,10 +13,16 @@ 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
|
-
|
17
|
-
|
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.
|
18
19
|
|
19
|
-
|
20
|
+
Supports Rails/ActiveRecord v3.1 (v3.2 recommended) higher (including
|
21
|
+
v5.0) and Ruby 1.9 and higher. Not all features work in ActiveRecord v3.1.
|
22
|
+
Support for this gem will only be for officially supported versions of
|
23
|
+
ActiveRecord and Ruby; others can try older versions of the gem.
|
24
|
+
|
25
|
+
##Using Cursors
|
20
26
|
|
21
27
|
PostgreSQLCursor was developed to take advantage of PostgreSQL's cursors. Cursors allow the program
|
22
28
|
to declare a cursor to run a given query returning "chunks" of rows to the application program while
|
@@ -43,6 +49,17 @@ Product.each_row_by_sql("select * from products") { |hash| Product.process(hash)
|
|
43
49
|
Product.each_instance_by_sql("select * from products") { |product| product.process }
|
44
50
|
```
|
45
51
|
|
52
|
+
Cursors must be run in a transaction if you need to fetch each row yourself
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
Product.transaction do
|
56
|
+
cursor = Product.all.each_row
|
57
|
+
row = cursor.fetch #=> {"id"=>"1"}
|
58
|
+
row = cursor.fetch(symbolize_keys:true) #=> {:id =>"2"}
|
59
|
+
cursor.close
|
60
|
+
end
|
61
|
+
```
|
62
|
+
|
46
63
|
All these methods take an options hash to control things more:
|
47
64
|
|
48
65
|
block_size:n The number of rows to fetch from the database each time (default 1000)
|
@@ -53,6 +70,7 @@ All these methods take an options hash to control things more:
|
|
53
70
|
PostgreSQL uses 0.1 (optimize for 10% of result set)
|
54
71
|
This library uses 1.0 (Optimize for 100% of the result set)
|
55
72
|
Do not override this value unless you understand it.
|
73
|
+
with_hold:boolean Keep the cursor "open" even after a commit.
|
56
74
|
|
57
75
|
Notes:
|
58
76
|
|
@@ -158,10 +176,22 @@ There are drawbacks with these methods:
|
|
158
176
|
* The query is rerun for each chunk (1000 rows), starting at the next id sequence.
|
159
177
|
* You cannot use overly complex queries as that will be rerun and incur more overhead.
|
160
178
|
|
179
|
+
### How it works
|
180
|
+
|
181
|
+
Under the covers, the library calls the PostgreSQL cursor operations
|
182
|
+
with the psuedo-code:
|
183
|
+
|
184
|
+
SET cursor_tuple_fraction TO 1.0;
|
185
|
+
DECLARE cursor_1 CURSOR WITH HOLD FOR select * from widgets;
|
186
|
+
loop
|
187
|
+
rows = FETCH 100 FROM cursor_1;
|
188
|
+
rows.each {|row| yield row}
|
189
|
+
until rows.size < 100;
|
190
|
+
CLOSE cursor_1;
|
161
191
|
|
162
192
|
##Meta
|
163
193
|
###Author
|
164
|
-
Allen Fair, [@allenfair](https://twitter
|
194
|
+
Allen Fair, [@allenfair](https://twitter.com/allenfair), http://github.com/afair
|
165
195
|
|
166
196
|
Thanks to:
|
167
197
|
|
@@ -179,6 +209,11 @@ Thanks to:
|
|
179
209
|
(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)
|
180
210
|
* Send me a pull request. Bonus points for topic branches.
|
181
211
|
|
212
|
+
###Code of Conduct
|
213
|
+
|
214
|
+
This project adheres to the [Open Code of Conduct](http://todogroup.org/opencodeofconduct/#postgresql_cursor/2016@allenfair.com).
|
215
|
+
By participating, you are expected to honor this code.
|
216
|
+
|
182
217
|
###Copyright
|
183
218
|
|
184
219
|
Copyright (c) 2010-2014 Allen Fair. See (MIT) LICENSE for details.
|
data/Rakefile
CHANGED
@@ -20,5 +20,5 @@ end
|
|
20
20
|
desc "Setup testing database and table"
|
21
21
|
task :setup do
|
22
22
|
sh %q(createdb postgresql_cursor_test)
|
23
|
-
sh %Q<echo "create table products ( id serial primary key);" | psql postgresql_cursor_test>
|
23
|
+
sh %Q<echo "create table products ( id serial primary key, data varchar);" | psql postgresql_cursor_test>
|
24
24
|
end
|
@@ -9,6 +9,7 @@ module PostgreSQLCursor
|
|
9
9
|
# block_size: 1..n - The number of rows to fetch per db block fetch
|
10
10
|
# while: value - Exits loop when block does not return this value.
|
11
11
|
# until: value - Exits loop when block returns this value.
|
12
|
+
# with_hold: boolean - Allows the query to remain open across commit points.
|
12
13
|
#
|
13
14
|
# Example:
|
14
15
|
# Post.each_row { |hash| Post.process(hash) }
|
@@ -41,6 +42,7 @@ module PostgreSQLCursor
|
|
41
42
|
# block_size: 1..n - The number of rows to fetch per db block fetch
|
42
43
|
# while: value - Exits loop when block does not return this value.
|
43
44
|
# until: value - Exits loop when block returns this value.
|
45
|
+
# with_hold: boolean - Allows the query to remain open across commit points.
|
44
46
|
#
|
45
47
|
# Example:
|
46
48
|
# Post.each_row_by_sql("select * from posts") { |hash| Post.process(hash) }
|
@@ -9,6 +9,7 @@
|
|
9
9
|
# block_size: 1..n - The number of rows to fetch per db block fetch
|
10
10
|
# while: value - Exits loop when block does not return this value.
|
11
11
|
# until: value - Exits loop when block returns this value.
|
12
|
+
# with_hold: boolean - Allows the query to remain open across commit points.
|
12
13
|
#
|
13
14
|
# Exmaples:
|
14
15
|
# PostgreSQLCursor::Cursor.new("select ...").each { |hash| ... }
|
@@ -30,6 +31,7 @@ module PostgreSQLCursor
|
|
30
31
|
# fraction: 0.1..1.0 - The cursor_tuple_fraction (default 1.0)
|
31
32
|
# block_size: 1..n - The number of rows to fetch per db block fetch
|
32
33
|
# Defaults to 1000
|
34
|
+
# with_hold - Allows the query to remain open across commit points.
|
33
35
|
#
|
34
36
|
# Examples
|
35
37
|
#
|
@@ -128,7 +130,7 @@ module PostgreSQLCursor
|
|
128
130
|
rescue Exception => e
|
129
131
|
raise e
|
130
132
|
ensure
|
131
|
-
close
|
133
|
+
close if @block
|
132
134
|
end
|
133
135
|
end
|
134
136
|
@count
|
@@ -160,16 +162,20 @@ module PostgreSQLCursor
|
|
160
162
|
def open
|
161
163
|
set_cursor_tuple_fraction
|
162
164
|
@cursor = @@cursor_seq += 1
|
163
|
-
|
165
|
+
hold = @options[:with_hold] ? 'with hold ' : ''
|
166
|
+
@result = @connection.execute("declare cursor_#{@cursor} cursor #{hold}for #{@sql}")
|
164
167
|
@block = []
|
165
168
|
end
|
166
169
|
|
167
170
|
# Public: Returns the next row from the cursor, or empty hash if end of results
|
168
171
|
#
|
169
172
|
# Returns a row as a hash of {'colname'=>value,...}
|
170
|
-
def fetch
|
173
|
+
def fetch(options={})
|
174
|
+
open unless @block
|
171
175
|
fetch_block if @block.size==0
|
172
|
-
@block.shift
|
176
|
+
row = @block.shift
|
177
|
+
row = row.symbolize_keys if row && options[:symbolize_keys]
|
178
|
+
row
|
173
179
|
end
|
174
180
|
|
175
181
|
# Private: Fetches the next block of rows into @block
|
data/postgresql_cursor.gemspec
CHANGED
@@ -18,8 +18,13 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
|
22
|
-
spec.add_dependency "activerecord", ">= 3.
|
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"
|
23
28
|
|
24
29
|
spec.add_development_dependency "pg"
|
25
30
|
spec.add_development_dependency "rake"
|
data/test-app/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Gemfile.lock
|
data/test-app/Gemfile
CHANGED
data/test/helper.rb
CHANGED
@@ -8,7 +8,7 @@ require 'postgresql_cursor'
|
|
8
8
|
ActiveRecord::Base.establish_connection(adapter: 'postgresql',
|
9
9
|
database: ENV['TEST_DATABASE'] || 'postgresql_cursor_test',
|
10
10
|
username: ENV['TEST_USER'] || ENV['USER'] || 'postgresql_cursor')
|
11
|
-
|
11
|
+
|
12
12
|
class Product < ActiveRecord::Base
|
13
13
|
# create table records (id serial primary key);
|
14
14
|
def self.generate(max=1_000)
|
@@ -78,5 +78,36 @@ class TestPostgresqlCursor < Minitest::Test
|
|
78
78
|
assert_equal 1000, r.size
|
79
79
|
assert_equal Fixnum, r.first.class
|
80
80
|
end
|
81
|
-
|
81
|
+
|
82
|
+
def test_with_hold
|
83
|
+
items = 0
|
84
|
+
Product.where("id < 4") .each_instance(with_hold: true, block_size:1) do |row|
|
85
|
+
Product.transaction do
|
86
|
+
row.update(data:Time.now.to_f.to_s)
|
87
|
+
items += 1
|
88
|
+
end
|
89
|
+
end
|
90
|
+
assert_equal 3, items
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_fetch_symbolize_keys
|
94
|
+
Product.transaction do
|
95
|
+
#cursor = PostgreSQLCursor::Cursor.new("select * from products order by 1")
|
96
|
+
cursor = Product.all.each_row
|
97
|
+
r = cursor.fetch
|
98
|
+
assert r.has_key?("id")
|
99
|
+
r = cursor.fetch(symbolize_keys:true)
|
100
|
+
assert r.has_key?(:id)
|
101
|
+
cursor.close
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_bad_sql
|
106
|
+
begin
|
107
|
+
ActiveRecord::Base.each_row_by_sql('select * from bad_table') { }
|
108
|
+
raise "Did Not Raise Expected Exception"
|
109
|
+
rescue Exception => e
|
110
|
+
assert_match(/bad_table/, e.message)
|
111
|
+
end
|
112
|
+
end
|
82
113
|
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.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Allen Fair
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-02-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 3.
|
19
|
+
version: 3.1.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.
|
26
|
+
version: 3.1.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: pg
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -83,7 +83,6 @@ files:
|
|
83
83
|
- LICENSE
|
84
84
|
- README.md
|
85
85
|
- Rakefile
|
86
|
-
- VERSION
|
87
86
|
- lib/postgresql_cursor.rb
|
88
87
|
- lib/postgresql_cursor/active_record/connection_adapters/postgresql_type_map.rb
|
89
88
|
- lib/postgresql_cursor/active_record/relation/cursor_iterators.rb
|
@@ -91,8 +90,8 @@ files:
|
|
91
90
|
- lib/postgresql_cursor/cursor.rb
|
92
91
|
- lib/postgresql_cursor/version.rb
|
93
92
|
- postgresql_cursor.gemspec
|
93
|
+
- test-app/.gitignore
|
94
94
|
- test-app/Gemfile
|
95
|
-
- test-app/Gemfile.lock
|
96
95
|
- test-app/app.rb
|
97
96
|
- test-app/run.sh
|
98
97
|
- test/helper.rb
|
@@ -117,7 +116,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
117
116
|
version: '0'
|
118
117
|
requirements: []
|
119
118
|
rubyforge_project:
|
120
|
-
rubygems_version: 2.
|
119
|
+
rubygems_version: 2.5.1
|
121
120
|
signing_key:
|
122
121
|
specification_version: 4
|
123
122
|
summary: ActiveRecord PostgreSQL Adapter extension for using a cursor to return a
|
data/VERSION
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
0.4.3
|
data/test-app/Gemfile.lock
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: /Users/allen/src/postgresql_cursor
|
3
|
-
specs:
|
4
|
-
postgresql_cursor (0.5.0)
|
5
|
-
activerecord (>= 3.2.0)
|
6
|
-
|
7
|
-
GEM
|
8
|
-
remote: https://rubygems.org/
|
9
|
-
specs:
|
10
|
-
activemodel (3.2.18)
|
11
|
-
activesupport (= 3.2.18)
|
12
|
-
builder (~> 3.0.0)
|
13
|
-
activerecord (3.2.18)
|
14
|
-
activemodel (= 3.2.18)
|
15
|
-
activesupport (= 3.2.18)
|
16
|
-
arel (~> 3.0.2)
|
17
|
-
tzinfo (~> 0.3.29)
|
18
|
-
activesupport (3.2.18)
|
19
|
-
i18n (~> 0.6, >= 0.6.4)
|
20
|
-
multi_json (~> 1.0)
|
21
|
-
arel (3.0.3)
|
22
|
-
builder (3.0.4)
|
23
|
-
i18n (0.6.9)
|
24
|
-
multi_json (1.10.1)
|
25
|
-
pg (0.17.1)
|
26
|
-
tzinfo (0.3.39)
|
27
|
-
|
28
|
-
PLATFORMS
|
29
|
-
ruby
|
30
|
-
|
31
|
-
DEPENDENCIES
|
32
|
-
activerecord (~> 3.2.0)
|
33
|
-
pg
|
34
|
-
postgresql_cursor!
|