simple-sql 0.4.23 → 0.4.24

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
2
  SHA256:
3
- metadata.gz: 27cc3b86bf4e72218ecc962cea6700d9a58e6a8ef594e15bfad16125bba792aa
4
- data.tar.gz: c4642ed511a3dd94511d6a56b9c22e5bce830e15a6ecb3e2ed3a74d192253e56
3
+ metadata.gz: 11f161fabbc3c7a0079e8e82288ed819bd75ac07f37a0c8a378032c9a9ca0b4c
4
+ data.tar.gz: 1792494b7417826ef4fe16523d3afb1bbd670d23db69be55ffeebc00adbcee6b
5
5
  SHA512:
6
- metadata.gz: 886016b412df3e5ad6d3eead65a1c1c81fe8c6eb98d868f2ae479a6c18b333d68f7661a21e17a566e7cfc151d8c844fca297c86bb7170ca423ff3d97a6bace12
7
- data.tar.gz: a651642b76dea15fe5d56c2311576a24d2992aa07d255aab244735c392fe7d9465a93aa255479ba2544e115162bf54b15378d718af40849d3645fbe8338eb051
6
+ metadata.gz: df9ac5a0c455dc17fc20e36c79e5905667f37c9dfe3a3b11debdbcd612ff4d64c84b8ebcf1a4e1a2c0e585a83623ca052c92a2e22dfb042a19be4bf553a7e40b
7
+ data.tar.gz: fcfe42ddfad18c3d0a019a07b3bd124595d0db5e64ac16e3c8244ac00ae3faad4e851915c74fd5418fb8b8285045c0338cf548638ad4446b8d3f683d3c87f180
@@ -32,29 +32,36 @@ module Simple::SQL::ConnectionAdapter
32
32
  # end
33
33
 
34
34
  def all(sql, *args, into: nil, &block)
35
- pg_result = exec_logged(sql, *args)
35
+ raise ArgumentError, "all no longer support blocks, use each instead." if block
36
+ rows = []
37
+ my_pg_source_oid = nil
36
38
 
37
- # enumerate the rows in pg_result. This returns either an Array of Hashes
38
- # (if into is set), or an array of row arrays or of singular values.
39
- #
40
- # Even if into is set to something different than a Hash, we'll convert
41
- # each row into a Hash initially, and only later convert it to the final
42
- # target type (via RowConverter.convert_ary). This is to allow to fill in
43
- # more entries later on.
44
- records = enumerate(pg_result, into: into)
39
+ each_without_conversion(sql, *args, into: into) do |row, pg_source_oid|
40
+ rows << row
41
+ my_pg_source_oid = pg_source_oid
42
+ end
45
43
 
46
- # optimization: If we wouldn't clear here the GC would do this later.
47
- pg_result.clear unless pg_result.autoclear?
44
+ record_set = convert_rows_to_result rows, into: into, pg_source_oid: my_pg_source_oid
48
45
 
49
46
  # [TODO] - resolve associations. Note that this is only possible if the type
50
47
  # is not an Array (i.e. into is nil)
51
48
 
52
49
  if sql.is_a?(Scope) && sql.paginated?
53
- records.send(:set_pagination_info, sql)
50
+ record_set.send(:set_pagination_info, sql)
54
51
  end
55
52
 
56
- records.each(&block) if block
57
- records
53
+ record_set
54
+ end
55
+
56
+ def each(sql, *args, into: nil)
57
+ raise ArgumentError, "Missing block" unless block_given?
58
+
59
+ each_without_conversion sql, *args, into: into do |row, pg_source_oid|
60
+ record = convert_row_to_record row, into: into, pg_source_oid: pg_source_oid
61
+ yield record
62
+ end
63
+
64
+ self
58
65
  end
59
66
 
60
67
  # Runs a query and prints the results via "table_print"
@@ -76,7 +83,7 @@ module Simple::SQL::ConnectionAdapter
76
83
  # returns an array <tt>[ <id>, <email> ]</tt> (or +nil+)
77
84
  def ask(sql, *args, into: nil)
78
85
  catch(:ok) do
79
- all(sql, *args, into: into) { |row| throw :ok, row }
86
+ each(sql, *args, into: into) { |row| throw :ok, row }
80
87
  nil
81
88
  end
82
89
  end
@@ -88,16 +95,16 @@ module Simple::SQL::ConnectionAdapter
88
95
  #
89
96
  # - <tt>Simple::SQL.locked(4711) { puts 'do work while locked' }
90
97
  def locked(lock_id)
91
- begin
92
- ask("SELECT pg_advisory_lock(#{lock_id})")
93
- yield
94
- ensure
95
- ask("SELECT pg_advisory_unlock(#{lock_id})")
96
- end
98
+ ask("SELECT pg_advisory_lock(#{lock_id})")
99
+ yield
100
+ ensure
101
+ ask("SELECT pg_advisory_unlock(#{lock_id})")
97
102
  end
98
103
 
99
104
  private
100
105
 
106
+ Result = ::Simple::SQL::Result
107
+ Decoder = ::Simple::SQL::Helpers::Decoder
101
108
  Encoder = ::Simple::SQL::Helpers::Encoder
102
109
 
103
110
  def exec_logged(sql_or_scope, *args)
@@ -115,20 +122,28 @@ module Simple::SQL::ConnectionAdapter
115
122
  end
116
123
  end
117
124
 
118
- Result = ::Simple::SQL::Result
119
- Decoder = ::Simple::SQL::Helpers::Decoder
120
-
121
- def enumerate(pg_result, into:)
122
- records = []
123
- pg_source_oid = nil
125
+ def each_without_conversion(sql, *args, into: nil)
126
+ pg_result = exec_logged(sql, *args)
124
127
 
125
128
  if pg_result.ntuples > 0 && pg_result.nfields > 0
126
129
  decoder = Decoder.new(self, pg_result, into: (into ? Hash : nil))
127
- pg_result.each_row { |row| records << decoder.decode(row) }
128
130
  pg_source_oid = pg_result.ftable(0)
131
+
132
+ pg_result.each_row do |row|
133
+ yield decoder.decode(row), pg_source_oid
134
+ end
129
135
  end
130
136
 
131
- Result.build(records, target_type: into, pg_source_oid: pg_source_oid)
137
+ # optimization: If we wouldn't clear here the GC would do this later.
138
+ pg_result.clear unless pg_result.autoclear?
139
+ end
140
+
141
+ def convert_row_to_record(row, into:, pg_source_oid:)
142
+ convert_rows_to_result([row], into: into, pg_source_oid: pg_source_oid).first
143
+ end
144
+
145
+ def convert_rows_to_result(rows, into:, pg_source_oid:)
146
+ Result.build(rows, target_type: into, pg_source_oid: pg_source_oid)
132
147
  end
133
148
 
134
149
  public
@@ -1,5 +1,5 @@
1
1
  module Simple
2
2
  module SQL
3
- VERSION = "0.4.23"
3
+ VERSION = "0.4.24"
4
4
  end
5
5
  end
data/simple-sql.gemspec CHANGED
@@ -42,4 +42,6 @@ Gem::Specification.new do |gem|
42
42
  gem.add_development_dependency 'database_cleaner', '~> 1'
43
43
  gem.add_development_dependency 'simplecov', '~> 0'
44
44
  gem.add_development_dependency 'awesome_print', '~> 0'
45
+
46
+ gem.add_development_dependency 'memory_profiler', '~> 0.9.12'
45
47
  end
@@ -15,23 +15,6 @@ describe "Simple::SQL.ask into: :struct" do
15
15
  r = SQL.all("SELECT * FROM users WHERE FALSE", into: Hash)
16
16
  expect(r).to eq([])
17
17
  end
18
-
19
- it "yields the results into a block" do
20
- received = []
21
- SQL.all("SELECT id FROM users", into: Hash) do |hsh|
22
- received << hsh
23
- end
24
- expect(received.length).to eq(USER_COUNT)
25
- expect(received.map(&:class).uniq).to eq([Hash])
26
- end
27
-
28
- it "does not yield if there is no match" do
29
- received = []
30
- SQL.all("SELECT id FROM users WHERE FALSE", into: Hash) do |hsh|
31
- received << hsh
32
- end
33
- expect(received.length).to eq(0)
34
- end
35
18
  end
36
19
 
37
20
  describe "into: :struct" do
@@ -0,0 +1,148 @@
1
+ require "spec_helper"
2
+
3
+ describe "Simple::SQL.each" do
4
+ context "when called without a block " do
5
+ it "raises an ArgumentError" do
6
+ expect {
7
+ SQL.each("SELECT id FROM users", into: Hash)
8
+ }.to raise_error(ArgumentError)
9
+ end
10
+ end
11
+
12
+ def generate_users!
13
+ 1.upto(USER_COUNT).map { create(:user) }
14
+ end
15
+
16
+ def each!(sql, into: nil)
17
+ @received = nil
18
+ SQL.each(sql, into: into) do |id|
19
+ @received ||= []
20
+ @received << id
21
+ end
22
+ end
23
+
24
+ let(:received) { @received }
25
+
26
+ describe "each into: nil" do
27
+ before { generate_users! }
28
+ context "when called with matches" do
29
+ it "receives rows as arrays" do
30
+ each! "SELECT id, id FROM users ORDER BY id"
31
+
32
+ expect(received).to eq(1.upto(USER_COUNT).map { |i| [ i,i ]})
33
+ end
34
+
35
+ it "receives single item row as individual objects" do
36
+ each! "SELECT id FROM users ORDER BY id"
37
+
38
+ expect(received).to eq(1.upto(USER_COUNT).to_a)
39
+ end
40
+ end
41
+
42
+ context 'when called with no matches' do
43
+ it "does not yield" do
44
+ each! "SELECT id FROM users WHERE FALSE"
45
+ expect(received).to be_nil
46
+ end
47
+ end
48
+ end
49
+
50
+ describe "each into: <something>" do
51
+ before { generate_users! }
52
+
53
+ it "receives rows as Hashes" do
54
+ each! "SELECT id, id AS dupe FROM users ORDER BY id", into: Hash
55
+
56
+ expect(received).to eq(1.upto(USER_COUNT).map { |i| { id: i, dupe: i }})
57
+ end
58
+
59
+ it "receives rows as immutable" do
60
+ each! "SELECT id, id AS dupe FROM users ORDER BY id", into: :immutable
61
+
62
+ expect(received.first.id).to eq(1)
63
+ expect(received[1].dupe).to eq(2)
64
+ expect(received.map(&:class).uniq).to eq([Simple::SQL::Helpers::Immutable])
65
+ end
66
+ end
67
+
68
+ xdescribe "memory usage: pending due to inconclusive results" do
69
+ it "generates a series" do
70
+ each! "SELECT a.n from generate_series(1, 100) as a(n)"
71
+ expect(received).to eq((1..100).to_a)
72
+ end
73
+
74
+ require 'memory_profiler'
75
+
76
+ def measure_retained_objects(msg, &block)
77
+ r = nil
78
+ report = MemoryProfiler.report do
79
+ r = yield
80
+ end
81
+
82
+ STDERR.puts "#{msg} Total allocated: #{report.total_allocated_memsize} bytes (#{report.total_allocated} objects)"
83
+ STDERR.puts "#{msg} Total retained: #{report.total_retained_memsize} bytes (#{report.total_retained} objects)"
84
+
85
+ report.total_retained_memsize
86
+ end
87
+
88
+ it "is using less memory than .all" do
89
+ sql_warmup = "SELECT a.n from generate_series(10000, 100) as a(n)"
90
+
91
+ SQL.all(sql_warmup, into: Hash)
92
+
93
+ SQL.each(sql_warmup) do |id|
94
+ :nop
95
+ end
96
+
97
+ cnt = 1000000
98
+ sql = "SELECT a.n from generate_series(#{cnt}, #{cnt}) as a(n)"
99
+
100
+ r = nil
101
+ retained_objects_all = measure_retained_objects "all" do
102
+ r = SQL.all(sql, into: Hash)
103
+ end
104
+
105
+ retained_objects_each = measure_retained_objects "each" do
106
+ r = SQL.each(sql) do |id|
107
+ :nop
108
+ end
109
+ end
110
+
111
+ expect(0).to eq "one"
112
+ end
113
+ end
114
+ end
115
+
116
+ __END__
117
+
118
+ describe "each into: X" do
119
+ it "calls the database" do
120
+ r = SQL.all("SELECT id FROM users", into: Hash)
121
+ expect(r).to be_a(Array)
122
+ expect(r.length).to eq(USER_COUNT)
123
+ expect(r.map(&:class).uniq).to eq([Hash])
124
+ end
125
+
126
+ it "returns an empty array when there is no match" do
127
+ r = SQL.all("SELECT * FROM users WHERE FALSE", into: Hash)
128
+ expect(r).to eq([])
129
+ end
130
+
131
+ it "yields the results into a block" do
132
+ received = []
133
+ SQL.all("SELECT id FROM users", into: Hash) do |hsh|
134
+ received << hsh
135
+ end
136
+ expect(received.length).to eq(USER_COUNT)
137
+ expect(received.map(&:class).uniq).to eq([Hash])
138
+ end
139
+
140
+ it "does not yield if there is no match" do
141
+ received = []
142
+ SQL.all("SELECT id FROM users WHERE FALSE", into: Hash) do |hsh|
143
+ received << hsh
144
+ end
145
+ expect(received.length).to eq(0)
146
+ end
147
+ end
148
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple-sql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.23
4
+ version: 0.4.24
5
5
  platform: ruby
6
6
  authors:
7
7
  - radiospiel
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2018-11-29 00:00:00.000000000 Z
12
+ date: 2018-12-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: pg_array_parser
@@ -165,6 +165,20 @@ dependencies:
165
165
  - - "~>"
166
166
  - !ruby/object:Gem::Version
167
167
  version: '0'
168
+ - !ruby/object:Gem::Dependency
169
+ name: memory_profiler
170
+ requirement: !ruby/object:Gem::Requirement
171
+ requirements:
172
+ - - "~>"
173
+ - !ruby/object:Gem::Version
174
+ version: 0.9.12
175
+ type: :development
176
+ prerelease: false
177
+ version_requirements: !ruby/object:Gem::Requirement
178
+ requirements:
179
+ - - "~>"
180
+ - !ruby/object:Gem::Version
181
+ version: 0.9.12
168
182
  description: SQL with a simple interface. Postgres only.
169
183
  email: eno@radiospiel.org
170
184
  executables: []
@@ -218,6 +232,7 @@ files:
218
232
  - spec/simple/sql/conversion_spec.rb
219
233
  - spec/simple/sql/duplicate_spec.rb
220
234
  - spec/simple/sql/duplicate_unique_spec.rb
235
+ - spec/simple/sql/each_spec.rb
221
236
  - spec/simple/sql/insert_spec.rb
222
237
  - spec/simple/sql/reflection_spec.rb
223
238
  - spec/simple/sql/scope_spec.rb
@@ -262,6 +277,7 @@ test_files:
262
277
  - spec/simple/sql/conversion_spec.rb
263
278
  - spec/simple/sql/duplicate_spec.rb
264
279
  - spec/simple/sql/duplicate_unique_spec.rb
280
+ - spec/simple/sql/each_spec.rb
265
281
  - spec/simple/sql/insert_spec.rb
266
282
  - spec/simple/sql/reflection_spec.rb
267
283
  - spec/simple/sql/scope_spec.rb