postgresql_cursor 0.5.1 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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!
|