simple-sql 0.4.23 → 0.4.24

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: 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