mini_sql 0.1.10 → 0.2.1
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/CHANGELOG +3 -0
- data/Guardfile +1 -0
- data/README.md +3 -3
- data/bench/timestamp_perf.rb +98 -61
- data/bench/topic_perf.rb +99 -85
- data/lib/mini_sql.rb +10 -1
- data/lib/mini_sql/connection.rb +13 -84
- data/lib/mini_sql/deserializer_cache.rb +2 -59
- data/lib/mini_sql/postgres/coders.rb +32 -0
- data/lib/mini_sql/postgres/connection.rb +123 -0
- data/lib/mini_sql/postgres/deserializer_cache.rb +67 -0
- data/lib/mini_sql/sqlite/connection.rb +68 -0
- data/lib/mini_sql/sqlite/deserializer_cache.rb +66 -0
- data/lib/mini_sql/version.rb +1 -1
- data/mini_sql.gemspec +3 -2
- metadata +29 -11
- data/lib/mini_sql/coders.rb +0 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8b9e4f7f96a77f3eaead87f509583b9bf604d49eec6e3caaf8a0234646eb208a
|
4
|
+
data.tar.gz: 94cac1f8ef8a82860686b1001761ef1b06a8be5228299427dc41d6be92d5a1c5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 04a54bb044404c6ed17bd425b0f7779afa130002850592ad1e91a249504c0b4b46e17453a59dab0e5e816a131669079742ca1baaae013259917bfec6dedab83f
|
7
|
+
data.tar.gz: fd4ceaf5f7e46efb8e5b067a54304c5a5eaa5d4216ff4c405bb64688b57b7844d7106dc3568e059b912994b193e197b83f9e81bfb1d5e91611b8c3ce5b65033f
|
data/CHANGELOG
ADDED
data/Guardfile
CHANGED
@@ -2,4 +2,5 @@ guard :minitest do
|
|
2
2
|
watch(%r{^test/(.*)_test\.rb$})
|
3
3
|
watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}#{m[2]}_test.rb" }
|
4
4
|
watch(%r{^test/test_helper\.rb$}) { 'test' }
|
5
|
+
watch("test/mini_sql/builder_tests.rb") { ['test/mini_sql/postgres/builder_test.rb', 'test/mini_sql/sqlite/builder_test.rb'] }
|
5
6
|
end
|
data/README.md
CHANGED
@@ -18,11 +18,11 @@ Or install it yourself as:
|
|
18
18
|
|
19
19
|
## Usage
|
20
20
|
|
21
|
-
MiniSql is a very simple, safe and fast SQL executor and mapper for PG.
|
21
|
+
MiniSql is a very simple, safe and fast SQL executor and mapper for PG and Sqlite.
|
22
22
|
|
23
23
|
```ruby
|
24
24
|
pg_conn = PG.connect(db_name: 'my_db')
|
25
|
-
conn = MiniSql::Connection.
|
25
|
+
conn = MiniSql::Connection.get(pg_conn)
|
26
26
|
|
27
27
|
puts conn.exec('update table set column = 1 where id in (1,2)')
|
28
28
|
# returns 2 if 2 rows changed
|
@@ -79,7 +79,7 @@ end
|
|
79
79
|
# ideally you want to remember to run r.clear here
|
80
80
|
|
81
81
|
# this is faster and safer
|
82
|
-
conn = MiniSql::Connection.
|
82
|
+
conn = MiniSql::Connection.get(pg_conn)
|
83
83
|
r = conn.query('select * from table')
|
84
84
|
|
85
85
|
r.each do |row|
|
data/bench/timestamp_perf.rb
CHANGED
@@ -11,7 +11,7 @@ gemfile do
|
|
11
11
|
gem 'benchmark-ips'
|
12
12
|
gem 'sequel', github: 'jeremyevans/sequel'
|
13
13
|
gem 'sequel_pg', github: 'jeremyevans/sequel_pg', require: 'sequel'
|
14
|
-
gem 'swift-db-postgres', github: 'deepfryed/swift-db-postgres'
|
14
|
+
# gem 'swift-db-postgres', github: 'deepfryed/swift-db-postgres'
|
15
15
|
end
|
16
16
|
|
17
17
|
require 'sequel'
|
@@ -74,8 +74,8 @@ $conn = ActiveRecord::Base.connection.raw_connection
|
|
74
74
|
def ar_pluck_times(l=1000)
|
75
75
|
s = +""
|
76
76
|
Timestamp.limit(l).order(:id).pluck(:time1, :time2).each do |time1, time2|
|
77
|
-
s << time1.
|
78
|
-
s << time2.
|
77
|
+
s << time1.to_f.to_s
|
78
|
+
s << time2.to_f.to_s
|
79
79
|
end
|
80
80
|
s
|
81
81
|
end
|
@@ -83,33 +83,57 @@ end
|
|
83
83
|
def ar_select_times(l=1000)
|
84
84
|
s = +""
|
85
85
|
Timestamp.limit(l).order(:id).select(:time1, :time2).each do |t|
|
86
|
-
s << t.time1.
|
87
|
-
s << t.time2.
|
86
|
+
s << t.time1.to_f.to_s
|
87
|
+
s << t.time2.to_f.to_s
|
88
88
|
end
|
89
89
|
s
|
90
90
|
end
|
91
91
|
|
92
92
|
$mini_sql = MiniSql::Connection.new($conn)
|
93
93
|
|
94
|
-
def
|
94
|
+
def pg_times_params(l=1000)
|
95
95
|
s = +""
|
96
96
|
# use the safe pattern here
|
97
97
|
r = $conn.async_exec_params(-"select time1, time2 from timestamps order by id limit $1", [l])
|
98
98
|
r.type_map = $mini_sql.type_map
|
99
|
-
|
100
|
-
|
101
|
-
|
99
|
+
|
100
|
+
i = 0
|
101
|
+
a = r.values
|
102
|
+
n = a.length
|
103
|
+
|
104
|
+
while i < n
|
105
|
+
s << a[i][0].to_f.to_s
|
106
|
+
s << a[i][1].to_f.to_s
|
107
|
+
i += 1
|
102
108
|
end
|
103
109
|
r.clear
|
104
110
|
s
|
105
111
|
end
|
106
112
|
|
113
|
+
def pg_times(l=1000)
|
114
|
+
s = +""
|
115
|
+
# use the safe pattern here
|
116
|
+
r = $conn.async_exec("select time1, time2 from timestamps order by id limit #{l}")
|
117
|
+
r.type_map = $mini_sql.type_map
|
118
|
+
|
119
|
+
i = 0
|
120
|
+
a = r.values
|
121
|
+
n = a.length
|
122
|
+
|
123
|
+
while i < n
|
124
|
+
s << a[i][0].to_f.to_s
|
125
|
+
s << a[i][1].to_f.to_s
|
126
|
+
i += 1
|
127
|
+
end
|
128
|
+
r.clear
|
129
|
+
s
|
130
|
+
end
|
107
131
|
|
108
132
|
def mini_sql_times(l=1000)
|
109
133
|
s = +""
|
110
134
|
$mini_sql.query(-"select time1, time2 from timestamps order by id limit ?", l).each do |t|
|
111
|
-
s << t.time1.
|
112
|
-
s << t.time2.
|
135
|
+
s << t.time1.to_f.to_s
|
136
|
+
s << t.time2.to_f.to_s
|
113
137
|
end
|
114
138
|
s
|
115
139
|
end
|
@@ -117,8 +141,8 @@ end
|
|
117
141
|
def sequel_times(l=1000)
|
118
142
|
s = +""
|
119
143
|
TimestampSequel.limit(l).order(:id).select(:time1, :time2).each do |t|
|
120
|
-
s << t.time1.
|
121
|
-
s << t.time2.
|
144
|
+
s << t.time1.to_f.to_s
|
145
|
+
s << t.time2.to_f.to_s
|
122
146
|
end
|
123
147
|
s
|
124
148
|
end
|
@@ -126,8 +150,17 @@ end
|
|
126
150
|
def sequel_pluck_times(l=1000)
|
127
151
|
s = +""
|
128
152
|
TimestampSequel.limit(l).order(:id).select_map([:time1, :time2]).each do |t|
|
129
|
-
s << t[0].
|
130
|
-
s << t[1].
|
153
|
+
s << t[0].to_f.to_s
|
154
|
+
s << t[1].to_f.to_s
|
155
|
+
end
|
156
|
+
s
|
157
|
+
end
|
158
|
+
|
159
|
+
def sequel_raw_times(l=1000)
|
160
|
+
s = +""
|
161
|
+
DB[-"select time1, time2 from timestamps order by id limit ?", l].map([:time1, :time2]).each do |time1, time2|
|
162
|
+
s << time1.to_f.to_s
|
163
|
+
s << time2.to_f.to_s
|
131
164
|
end
|
132
165
|
s
|
133
166
|
end
|
@@ -138,58 +171,65 @@ def mini_sql_times_single(l=1000)
|
|
138
171
|
i = 0
|
139
172
|
r = $mini_sql.query_single(-"select time1, time2 from timestamps order by id limit ?", l)
|
140
173
|
while i < r.length
|
141
|
-
s << r[i].
|
142
|
-
s << r[i+1].
|
174
|
+
s << r[i].to_f.to_s
|
175
|
+
s << r[i+1].to_f.to_s
|
143
176
|
i += 2
|
144
177
|
end
|
145
178
|
s
|
146
179
|
end
|
147
180
|
|
148
|
-
$swift = Swift::DB::Postgres.new(db: "test_db", user: 'sam', password: 'password')
|
149
|
-
|
150
|
-
def swift_select_times(l=1000)
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
end
|
181
|
+
# $swift = Swift::DB::Postgres.new(db: "test_db", user: 'sam', password: 'password')
|
182
|
+
#
|
183
|
+
# def swift_select_times(l=1000)
|
184
|
+
# s = ""
|
185
|
+
# r = $swift.execute("select time1, time2 from timestamps order by id limit $1", l)
|
186
|
+
# r.each do |row|
|
187
|
+
# s << row[:time1].to_f.to_s
|
188
|
+
# s << row[:time2].to_f.to_s
|
189
|
+
# end
|
190
|
+
# s
|
191
|
+
# end
|
159
192
|
|
160
193
|
|
161
194
|
results = [
|
162
195
|
ar_select_times,
|
163
196
|
ar_pluck_times,
|
164
197
|
pg_times,
|
198
|
+
pg_times_params,
|
165
199
|
mini_sql_times,
|
166
200
|
mini_sql_times_single,
|
167
201
|
sequel_times,
|
168
202
|
sequel_pluck_times,
|
203
|
+
sequel_raw_times,
|
204
|
+
# can not compare correctly as it is returning DateTime not Time
|
205
|
+
# swift_select_times.gsub("+00:00", "Z")
|
169
206
|
]
|
170
207
|
|
171
208
|
exit(-1) unless results.uniq.length == 1
|
172
209
|
|
173
210
|
Benchmark.ips do |r|
|
174
|
-
r.
|
211
|
+
r.warmup = 10
|
212
|
+
r.time = 5
|
213
|
+
|
214
|
+
r.report("mini_sql query_single times") do |n|
|
175
215
|
while n > 0
|
176
|
-
|
216
|
+
mini_sql_times_single
|
177
217
|
n -= 1
|
178
218
|
end
|
179
219
|
end
|
180
|
-
r.report("
|
220
|
+
r.report("sequel times") do |n|
|
181
221
|
while n > 0
|
182
|
-
|
222
|
+
sequel_times
|
183
223
|
n -= 1
|
184
224
|
end
|
185
225
|
end
|
186
|
-
r.report("
|
226
|
+
r.report("pg times async_exec values") do |n|
|
187
227
|
while n > 0
|
188
|
-
|
228
|
+
pg_times_params
|
189
229
|
n -= 1
|
190
230
|
end
|
191
231
|
end
|
192
|
-
r.report("pg times") do |n|
|
232
|
+
r.report("pg times async_exec_params values") do |n|
|
193
233
|
while n > 0
|
194
234
|
pg_times
|
195
235
|
n -= 1
|
@@ -207,43 +247,40 @@ Benchmark.ips do |r|
|
|
207
247
|
n -= 1
|
208
248
|
end
|
209
249
|
end
|
210
|
-
r.report("
|
250
|
+
r.report("sequel raw times") do |n|
|
211
251
|
while n > 0
|
212
|
-
|
252
|
+
sequel_raw_times
|
213
253
|
n -= 1
|
214
254
|
end
|
215
255
|
end
|
216
|
-
r.report("
|
256
|
+
r.report("ar select times") do |n|
|
217
257
|
while n > 0
|
218
|
-
|
258
|
+
ar_select_times
|
259
|
+
n -= 1
|
260
|
+
end
|
261
|
+
end
|
262
|
+
r.report("ar pluck times") do |n|
|
263
|
+
while n > 0
|
264
|
+
ar_pluck_times
|
219
265
|
n -= 1
|
220
266
|
end
|
221
267
|
end
|
222
268
|
r.compare!
|
223
269
|
end
|
224
270
|
|
225
|
-
#
|
226
|
-
# swift_select_times: 222.4 i/s
|
227
|
-
# mini_sql query_single times: 99.8 i/s - 2.23x slower
|
228
|
-
# mini sql times: 97.1 i/s - 2.29x slower
|
229
|
-
# pg times: 87.0 i/s - 2.56x slower
|
230
|
-
# ar pluck times: 31.5 i/s - 7.05x slower
|
231
|
-
# ar select times: 22.5 i/s - 9.89x slower
|
232
|
-
# sequel pluck times: 10.9 i/s - 20.42x slower
|
233
|
-
# sequel times: 10.4 i/s - 21.37x slower
|
271
|
+
# Linux x64 numbers
|
234
272
|
#
|
235
|
-
# NOTE PG version 1.0.0 has a slower time materializer
|
236
273
|
#
|
237
|
-
#
|
238
|
-
#
|
239
|
-
#
|
240
|
-
#
|
241
|
-
# mini sql times:
|
242
|
-
#
|
243
|
-
#
|
244
|
-
#
|
245
|
-
#
|
246
|
-
# sequel times: 10.4 i/s - 21.43x slower
|
274
|
+
# pg times async_exec values: 441.6 i/s
|
275
|
+
# pg times async_exec_params values: 438.5 i/s - same-ish: difference falls within error
|
276
|
+
# mini_sql query_single times: 436.3 i/s - same-ish: difference falls within error
|
277
|
+
# sequel raw times: 426.1 i/s - same-ish: difference falls within error
|
278
|
+
# mini sql times: 417.1 i/s - 1.06x slower
|
279
|
+
# sequel pluck times: 414.0 i/s - 1.07x slower
|
280
|
+
# sequel times: 378.5 i/s - 1.17x slower
|
281
|
+
# ar pluck times: 30.8 i/s - 14.35x slower
|
282
|
+
# ar select times: 22.7 i/s - 19.44x slower
|
247
283
|
#
|
248
|
-
|
249
|
-
#
|
284
|
+
|
285
|
+
# NOTE PG version 1.0.0 has a much slower time materializer
|
286
|
+
# NOTE 2: on Mac numbers are far closer Time parsing on mac is slow
|
data/bench/topic_perf.rb
CHANGED
@@ -2,15 +2,16 @@ require 'bundler/inline'
|
|
2
2
|
|
3
3
|
gemfile do
|
4
4
|
source 'https://rubygems.org'
|
5
|
-
gem 'pg'
|
5
|
+
gem 'pg', github: 'ged/ruby-pg'
|
6
6
|
gem 'mini_sql', path: '../'
|
7
7
|
gem 'activesupport'
|
8
8
|
gem 'activerecord'
|
9
9
|
gem 'activemodel'
|
10
10
|
gem 'memory_profiler'
|
11
11
|
gem 'benchmark-ips'
|
12
|
-
gem 'sequel'
|
13
|
-
gem 'sequel_pg', require: 'sequel'
|
12
|
+
gem 'sequel', github: 'jeremyevans/sequel'
|
13
|
+
gem 'sequel_pg', github: 'jeremyevans/sequel_pg', require: 'sequel'
|
14
|
+
gem 'swift-db-postgres', github: 'deepfryed/swift-db-postgres'
|
14
15
|
end
|
15
16
|
|
16
17
|
require 'sequel'
|
@@ -131,9 +132,17 @@ def pg_title_id
|
|
131
132
|
s = +""
|
132
133
|
# use the safe pattern here
|
133
134
|
r = $conn.async_exec(-"select id, title from topics order by id limit 1000")
|
134
|
-
|
135
|
-
|
136
|
-
|
135
|
+
|
136
|
+
# this seems fastest despite extra arrays, cause array of arrays is generated
|
137
|
+
# in c code
|
138
|
+
values = r.values
|
139
|
+
|
140
|
+
i = 0
|
141
|
+
l = values.length
|
142
|
+
while i < l
|
143
|
+
s << values[i][0].to_s
|
144
|
+
s << values[i][1]
|
145
|
+
i += 1
|
137
146
|
end
|
138
147
|
r.clear
|
139
148
|
s
|
@@ -181,6 +190,21 @@ def mini_sql_title_id_query_single
|
|
181
190
|
s
|
182
191
|
end
|
183
192
|
|
193
|
+
# connects over unix socket
|
194
|
+
$swift = Swift::DB::Postgres.new(db: "test_db")
|
195
|
+
|
196
|
+
def swift_select_title_id(l=1000)
|
197
|
+
s = ""
|
198
|
+
i = 0
|
199
|
+
r = $swift.execute("select id, title from topics order by id limit 1000")
|
200
|
+
while i < r.selected_rows
|
201
|
+
s << r.get(i, 0).to_s
|
202
|
+
s << r.get(i, 1)
|
203
|
+
i += 1
|
204
|
+
end
|
205
|
+
s
|
206
|
+
end
|
207
|
+
|
184
208
|
results = [
|
185
209
|
ar_title_id,
|
186
210
|
ar_title_id_pluck,
|
@@ -188,101 +212,108 @@ results = [
|
|
188
212
|
mini_sql_title_id,
|
189
213
|
sequel_pluck_title_id,
|
190
214
|
sequel_select_title_id,
|
191
|
-
mini_sql_title_id_query_single
|
215
|
+
mini_sql_title_id_query_single,
|
216
|
+
swift_select_title_id
|
192
217
|
]
|
193
218
|
|
194
219
|
exit(-1) unless results.uniq.length == 1
|
195
220
|
|
196
221
|
|
197
|
-
def wide_topic_ar
|
198
|
-
Topic.first
|
199
|
-
end
|
200
|
-
|
201
|
-
def wide_topic_pg
|
202
|
-
r = $conn.async_exec("select * from topics limit 1")
|
203
|
-
row = r.first
|
204
|
-
r.clear
|
205
|
-
row
|
206
|
-
end
|
207
|
-
|
208
|
-
def wide_topic_sequel
|
209
|
-
TopicSequel.first
|
210
|
-
end
|
211
|
-
|
212
|
-
def wide_topic_mini_sql
|
213
|
-
$conn.query("select * from topics limit 1").first
|
214
|
-
end
|
215
|
-
|
216
222
|
Benchmark.ips do |r|
|
217
|
-
r.report("
|
223
|
+
r.report("ar select title id") do |n|
|
218
224
|
while n > 0
|
219
|
-
|
225
|
+
ar_title_id
|
220
226
|
n -= 1
|
221
227
|
end
|
222
228
|
end
|
223
|
-
r.report("
|
229
|
+
r.report("ar select title id pluck") do |n|
|
224
230
|
while n > 0
|
225
|
-
|
231
|
+
ar_title_id_pluck
|
226
232
|
n -= 1
|
227
233
|
end
|
228
234
|
end
|
229
|
-
r.report("
|
235
|
+
r.report("sequel title id select") do |n|
|
230
236
|
while n > 0
|
231
|
-
|
237
|
+
sequel_select_title_id
|
232
238
|
n -= 1
|
233
239
|
end
|
234
240
|
end
|
235
|
-
r.report("
|
241
|
+
r.report("pg select title id") do |n|
|
236
242
|
while n > 0
|
237
|
-
|
243
|
+
pg_title_id
|
238
244
|
n -= 1
|
239
245
|
end
|
240
246
|
end
|
241
|
-
r.
|
242
|
-
end
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
Benchmark.ips do |r|
|
247
|
-
r.report("ar select title id") do |n|
|
247
|
+
r.report("mini_sql select title id") do |n|
|
248
248
|
while n > 0
|
249
|
-
|
249
|
+
mini_sql_title_id
|
250
250
|
n -= 1
|
251
251
|
end
|
252
252
|
end
|
253
|
-
r.report("
|
253
|
+
r.report("sequel title id pluck") do |n|
|
254
254
|
while n > 0
|
255
|
-
|
255
|
+
sequel_pluck_title_id
|
256
256
|
n -= 1
|
257
257
|
end
|
258
258
|
end
|
259
|
-
r.report("
|
259
|
+
r.report("mini_sql query_single title id") do |n|
|
260
260
|
while n > 0
|
261
|
-
|
261
|
+
mini_sql_title_id_query_single
|
262
262
|
n -= 1
|
263
263
|
end
|
264
264
|
end
|
265
|
-
r.report("
|
265
|
+
r.report("swift title id") do |n|
|
266
266
|
while n > 0
|
267
|
-
|
267
|
+
swift_select_title_id
|
268
268
|
n -= 1
|
269
269
|
end
|
270
270
|
end
|
271
|
-
r.
|
271
|
+
r.compare!
|
272
|
+
end
|
273
|
+
|
274
|
+
|
275
|
+
|
276
|
+
def wide_topic_ar
|
277
|
+
Topic.first
|
278
|
+
end
|
279
|
+
|
280
|
+
def wide_topic_pg
|
281
|
+
r = $conn.async_exec("select * from topics limit 1")
|
282
|
+
row = r.first
|
283
|
+
r.clear
|
284
|
+
row
|
285
|
+
end
|
286
|
+
|
287
|
+
def wide_topic_sequel
|
288
|
+
TopicSequel.first
|
289
|
+
end
|
290
|
+
|
291
|
+
def wide_topic_mini_sql
|
292
|
+
$conn.query("select * from topics limit 1").first
|
293
|
+
end
|
294
|
+
|
295
|
+
Benchmark.ips do |r|
|
296
|
+
r.report("wide topic ar") do |n|
|
272
297
|
while n > 0
|
273
|
-
|
298
|
+
wide_topic_ar
|
274
299
|
n -= 1
|
275
300
|
end
|
276
301
|
end
|
277
|
-
r.report("
|
302
|
+
r.report("wide topic sequel") do |n|
|
278
303
|
while n > 0
|
279
|
-
|
304
|
+
wide_topic_sequel
|
280
305
|
n -= 1
|
281
306
|
end
|
282
307
|
end
|
283
|
-
r.report("
|
308
|
+
r.report("wide topic pg") do |n|
|
284
309
|
while n > 0
|
285
|
-
|
310
|
+
wide_topic_pg
|
311
|
+
n -= 1
|
312
|
+
end
|
313
|
+
end
|
314
|
+
r.report("wide topic mini sql") do |n|
|
315
|
+
while n > 0
|
316
|
+
wide_topic_mini_sql
|
286
317
|
n -= 1
|
287
318
|
end
|
288
319
|
end
|
@@ -290,41 +321,24 @@ Benchmark.ips do |r|
|
|
290
321
|
end
|
291
322
|
|
292
323
|
|
293
|
-
# Calculating -------------------------------------
|
294
|
-
# wide topic ar 2.383k (± 4.9%) i/s - 12.005k in 5.050490s
|
295
|
-
# wide topic sequel 3.449k (± 3.2%) i/s - 17.591k in 5.104951s
|
296
|
-
# wide topic pg 7.345k (± 1.2%) i/s - 37.352k in 5.086015s
|
297
|
-
# wide topic mini sql 7.536k (± 1.4%) i/s - 38.220k in 5.072834s
|
298
|
-
#
|
299
324
|
# Comparison:
|
300
|
-
#
|
301
|
-
#
|
302
|
-
#
|
303
|
-
#
|
325
|
+
# pg select title id: 1519.7 i/s
|
326
|
+
# mini_sql query_single title id: 1335.0 i/s - 1.14x slower
|
327
|
+
# sequel title id pluck: 1261.6 i/s - 1.20x slower
|
328
|
+
# mini_sql select title id: 1188.6 i/s - 1.28x slower
|
329
|
+
# swift title id: 1077.5 i/s - 1.41x slower
|
330
|
+
# sequel title id select: 969.7 i/s - 1.57x slower
|
331
|
+
# ar select title id pluck: 738.7 i/s - 2.06x slower
|
332
|
+
# ar select title id: 149.6 i/s - 10.16x slower
|
304
333
|
#
|
305
|
-
# Calculating -------------------------------------
|
306
|
-
# ar select title id 131.572 (± 3.8%) i/s - 658.000 in 5.008231s
|
307
|
-
# ar select title id pluck
|
308
|
-
# 696.233 (± 3.7%) i/s - 3.519k in 5.061335s
|
309
|
-
# sequel title id select
|
310
|
-
# 916.841 (± 3.7%) i/s - 4.655k in 5.084499s
|
311
|
-
# pg select title id 1.002k (± 4.0%) i/s - 5.044k in 5.040584s
|
312
|
-
# mini_sql select title id
|
313
|
-
# 1.106k (± 2.4%) i/s - 5.618k in 5.084423s
|
314
|
-
# sequel title id pluck
|
315
|
-
# 1.181k (± 3.5%) i/s - 5.980k in 5.069815s
|
316
|
-
# mini_sql query_single title id
|
317
|
-
# 1.171k (± 3.1%) i/s - 5.880k in 5.025793s
|
318
334
|
#
|
319
335
|
# Comparison:
|
320
|
-
#
|
321
|
-
#
|
322
|
-
#
|
323
|
-
#
|
324
|
-
|
325
|
-
|
326
|
-
# ar select title id: 131.6 i/s - 8.98x slower
|
327
|
-
#
|
336
|
+
# wide topic pg: 7474.0 i/s
|
337
|
+
# wide topic mini sql: 7355.2 i/s - same-ish: difference falls within error
|
338
|
+
# wide topic sequel: 5696.8 i/s - 1.31x slower
|
339
|
+
# wide topic ar: 2515.0 i/s - 2.97x slower
|
340
|
+
|
341
|
+
|
328
342
|
|
329
343
|
# to run deep analysis run
|
330
344
|
# MemoryProfiler.report do
|
data/lib/mini_sql.rb
CHANGED
@@ -10,5 +10,14 @@ require_relative "mini_sql/builder"
|
|
10
10
|
require_relative "mini_sql/inline_param_encoder"
|
11
11
|
|
12
12
|
module MiniSql
|
13
|
-
|
13
|
+
module Postgres
|
14
|
+
autoload :Coders, "mini_sql/postgres/coders"
|
15
|
+
autoload :Connection, "mini_sql/postgres/connection"
|
16
|
+
autoload :DeserializerCache, "mini_sql/postgres/deserializer_cache"
|
17
|
+
end
|
18
|
+
|
19
|
+
module Sqlite
|
20
|
+
autoload :Connection, "mini_sql/sqlite/connection"
|
21
|
+
autoload :DeserializerCache, "mini_sql/sqlite/deserializer_cache"
|
22
|
+
end
|
14
23
|
end
|
data/lib/mini_sql/connection.rb
CHANGED
@@ -2,47 +2,15 @@
|
|
2
2
|
|
3
3
|
module MiniSql
|
4
4
|
class Connection
|
5
|
-
attr_reader :raw_connection, :type_map, :param_encoder
|
6
5
|
|
7
|
-
def self.
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
map.add_coder(MiniSql::Coders::NumericCoder.new(name: "numeric", oid: 1700, format: 0))
|
16
|
-
map.add_coder(MiniSql::Coders::IPAddrCoder.new(name: "inet", oid: 869, format: 0))
|
17
|
-
map.add_coder(MiniSql::Coders::IPAddrCoder.new(name: "cidr", oid: 650, format: 0))
|
18
|
-
map.add_coder(PG::TextDecoder::String.new(name: "tsvector", oid: 3614, format: 0))
|
19
|
-
|
20
|
-
map.rm_coder(0, 1114)
|
21
|
-
if defined? PG::TextDecoder::TimestampUtc
|
22
|
-
# treat timestamp without zone as utc
|
23
|
-
# new to PG 1.1
|
24
|
-
map.add_coder(PG::TextDecoder::TimestampUtc.new(name: "timestamp", oid: 1114, format: 0))
|
25
|
-
else
|
26
|
-
map.add_coder(MiniSql::Coders::TimestampUtc.new(name: "timestamp", oid: 1114, format: 0))
|
27
|
-
end
|
28
|
-
map
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
# Initialize a new MiniSql::Connection object
|
33
|
-
#
|
34
|
-
# @param raw_connection [PG::Connection] an active connection to PG
|
35
|
-
# @param deserializer_cache [MiniSql::DeserializerCache] a cache of field names to deserializer, can be nil
|
36
|
-
# @param type_map [PG::TypeMap] a type mapper for all results returned, can be nil
|
37
|
-
def initialize(raw_connection, deserializer_cache: nil, param_encoder: nil)
|
38
|
-
# TODO adapter to support other databases
|
39
|
-
@raw_connection = raw_connection
|
40
|
-
@deserializer_cache = deserializer_cache || Connection.default_deserializer_cache
|
41
|
-
@param_encoder = param_encoder || InlineParamEncoder.new(self)
|
42
|
-
end
|
43
|
-
|
44
|
-
def type_map
|
45
|
-
@type_map ||= self.class.type_map(raw_connection)
|
6
|
+
def self.get(raw_connection, options = {})
|
7
|
+
if (defined? ::PG::Connection) && (PG::Connection === raw_connection)
|
8
|
+
Postgres::Connection.new(raw_connection, options)
|
9
|
+
elsif (defined? ::SQLite3::Database) && (SQLite3::Database === raw_connection)
|
10
|
+
Sqlite::Connection.new(raw_connection, options)
|
11
|
+
else
|
12
|
+
raise ArgumentError, 'unknown connection type!'
|
13
|
+
end
|
46
14
|
end
|
47
15
|
|
48
16
|
# Returns a flat array containing all results.
|
@@ -52,49 +20,19 @@ module MiniSql
|
|
52
20
|
# @param params [Array or Hash], params to apply to query
|
53
21
|
# @return [Object] a flat array containing all results
|
54
22
|
def query_single(sql, *params)
|
55
|
-
|
56
|
-
result.type_map = type_map
|
57
|
-
if result.nfields == 1
|
58
|
-
result.column_values(0)
|
59
|
-
else
|
60
|
-
array = []
|
61
|
-
f = 0
|
62
|
-
row = 0
|
63
|
-
while row < result.ntuples
|
64
|
-
while f < result.nfields
|
65
|
-
array << result.getvalue(row, f)
|
66
|
-
f += 1
|
67
|
-
end
|
68
|
-
f = 0
|
69
|
-
row += 1
|
70
|
-
end
|
71
|
-
array
|
72
|
-
end
|
73
|
-
ensure
|
74
|
-
result.clear if result
|
23
|
+
raise NotImplementedError, "must be implemented by child connection"
|
75
24
|
end
|
76
25
|
|
77
26
|
def query(sql, *params)
|
78
|
-
|
79
|
-
result.type_map = type_map
|
80
|
-
@deserializer_cache.materialize(result)
|
81
|
-
ensure
|
82
|
-
result.clear if result
|
27
|
+
raise NotImplementedError, "must be implemented by child connection"
|
83
28
|
end
|
84
29
|
|
85
30
|
def exec(sql, *params)
|
86
|
-
|
87
|
-
result.cmd_tuples
|
88
|
-
ensure
|
89
|
-
result.clear if result
|
31
|
+
raise NotImplementedError, "must be implemented by child connection"
|
90
32
|
end
|
91
33
|
|
92
34
|
def query_hash(sql, *params)
|
93
|
-
|
94
|
-
result.type_map = type_map
|
95
|
-
result.to_a
|
96
|
-
ensure
|
97
|
-
result.clear
|
35
|
+
raise NotImplementedError, "must be implemented by child connection"
|
98
36
|
end
|
99
37
|
|
100
38
|
def build(sql)
|
@@ -102,16 +40,7 @@ module MiniSql
|
|
102
40
|
end
|
103
41
|
|
104
42
|
def escape_string(str)
|
105
|
-
|
106
|
-
end
|
107
|
-
|
108
|
-
private
|
109
|
-
|
110
|
-
def run(sql, params)
|
111
|
-
if params && params.length > 0
|
112
|
-
sql = param_encoder.encode(sql, *params)
|
113
|
-
end
|
114
|
-
raw_connection.async_exec(sql)
|
43
|
+
raise NotImplementedError, "must be implemented by child connection"
|
115
44
|
end
|
116
45
|
|
117
46
|
end
|
@@ -1,65 +1,8 @@
|
|
1
1
|
module MiniSql
|
2
2
|
class DeserializerCache
|
3
|
-
|
4
|
-
DEFAULT_MAX_SIZE = 500
|
5
|
-
|
6
|
-
def initialize(max_size = nil)
|
7
|
-
@cache = {}
|
8
|
-
@max_size = max_size || DEFAULT_MAX_SIZE
|
9
|
-
end
|
10
|
-
|
3
|
+
# method takes a raw result and converts to proper objects
|
11
4
|
def materialize(result)
|
12
|
-
|
13
|
-
return [] if result.ntuples == 0
|
14
|
-
|
15
|
-
key = result.fields
|
16
|
-
|
17
|
-
# trivial fast LRU implementation
|
18
|
-
materializer = @cache.delete(key)
|
19
|
-
if materializer
|
20
|
-
@cache[key] = materializer
|
21
|
-
else
|
22
|
-
materializer = @cache[key] = new_row_matrializer(result)
|
23
|
-
@cache.shift if @cache.length > @max_size
|
24
|
-
end
|
25
|
-
|
26
|
-
i = 0
|
27
|
-
r = []
|
28
|
-
# quicker loop
|
29
|
-
while i < result.ntuples
|
30
|
-
r << materializer.materialize(result, i)
|
31
|
-
i += 1
|
32
|
-
end
|
33
|
-
r
|
34
|
-
end
|
35
|
-
|
36
|
-
private
|
37
|
-
|
38
|
-
def new_row_matrializer(result)
|
39
|
-
fields = result.fields
|
40
|
-
|
41
|
-
Class.new do
|
42
|
-
attr_accessor(*fields)
|
43
|
-
|
44
|
-
# AM serializer support
|
45
|
-
alias :read_attribute_for_serialization :send
|
46
|
-
|
47
|
-
def to_h
|
48
|
-
r = {}
|
49
|
-
instance_variables.each do |f|
|
50
|
-
r[f.to_s.sub('@','').to_sym] = instance_variable_get(f)
|
51
|
-
end
|
52
|
-
r
|
53
|
-
end
|
54
|
-
|
55
|
-
instance_eval <<~RUBY
|
56
|
-
def materialize(pg_result, index)
|
57
|
-
r = self.new
|
58
|
-
#{col=-1; fields.map{|f| "r.#{f} = pg_result.getvalue(index, #{col+=1})"}.join("; ")}
|
59
|
-
r
|
60
|
-
end
|
61
|
-
RUBY
|
62
|
-
end
|
5
|
+
raise NotImplementedError, "must be implemented by child"
|
63
6
|
end
|
64
7
|
end
|
65
8
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module MiniSql
|
2
|
+
module Postgres
|
3
|
+
module Coders
|
4
|
+
class NumericCoder < PG::SimpleDecoder
|
5
|
+
def decode(string, tuple = nil, field = nil)
|
6
|
+
BigDecimal(string)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class IPAddrCoder < PG::SimpleDecoder
|
11
|
+
def decode(string, tuple = nil, field = nil)
|
12
|
+
IPAddr.new(string)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class TimestampUtc < PG::SimpleDecoder
|
17
|
+
# exact same implementation as Rails here
|
18
|
+
ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/
|
19
|
+
|
20
|
+
def decode(string, tuple = nil, field = nil)
|
21
|
+
if string =~ ISO_DATETIME
|
22
|
+
microsec = ($7.to_r * 1_000_000).to_i
|
23
|
+
Time.utc $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec
|
24
|
+
else
|
25
|
+
STDERR.puts "unexpected date time format #{string}"
|
26
|
+
string
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MiniSql
|
4
|
+
module Postgres
|
5
|
+
class Connection < MiniSql::Connection
|
6
|
+
attr_reader :raw_connection, :type_map, :param_encoder
|
7
|
+
|
8
|
+
def self.default_deserializer_cache
|
9
|
+
@deserializer_cache ||= DeserializerCache.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.type_map(conn)
|
13
|
+
@type_map ||=
|
14
|
+
begin
|
15
|
+
map = PG::BasicTypeMapForResults.new(conn)
|
16
|
+
map.add_coder(MiniSql::Postgres::Coders::NumericCoder.new(name: "numeric", oid: 1700, format: 0))
|
17
|
+
map.add_coder(MiniSql::Postgres::Coders::IPAddrCoder.new(name: "inet", oid: 869, format: 0))
|
18
|
+
map.add_coder(MiniSql::Postgres::Coders::IPAddrCoder.new(name: "cidr", oid: 650, format: 0))
|
19
|
+
map.add_coder(PG::TextDecoder::String.new(name: "tsvector", oid: 3614, format: 0))
|
20
|
+
|
21
|
+
map.rm_coder(0, 1114)
|
22
|
+
if defined? PG::TextDecoder::TimestampUtc
|
23
|
+
# treat timestamp without zone as utc
|
24
|
+
# new to PG 1.1
|
25
|
+
map.add_coder(PG::TextDecoder::TimestampUtc.new(name: "timestamp", oid: 1114, format: 0))
|
26
|
+
else
|
27
|
+
map.add_coder(MiniSql::Postgres::Coders::TimestampUtc.new(name: "timestamp", oid: 1114, format: 0))
|
28
|
+
end
|
29
|
+
map
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Initialize a new MiniSql::Postgres::Connection object
|
34
|
+
#
|
35
|
+
# @param raw_connection [PG::Connection] an active connection to PG
|
36
|
+
# @param deserializer_cache [MiniSql::DeserializerCache] a cache of field names to deserializer, can be nil
|
37
|
+
# @param type_map [PG::TypeMap] a type mapper for all results returned, can be nil
|
38
|
+
def initialize(raw_connection, args = nil)
|
39
|
+
@raw_connection = raw_connection
|
40
|
+
@deserializer_cache = (args && args[:deserializer_cache]) || self.class.default_deserializer_cache
|
41
|
+
@param_encoder = (args && args[:param_encoder]) || InlineParamEncoder.new(self)
|
42
|
+
end
|
43
|
+
|
44
|
+
def type_map
|
45
|
+
@type_map ||= self.class.type_map(raw_connection)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns a flat array containing all results.
|
49
|
+
# Note, if selecting multiple columns array will be flattened
|
50
|
+
#
|
51
|
+
# @param sql [String] the query to run
|
52
|
+
# @param params [Array or Hash], params to apply to query
|
53
|
+
# @return [Object] a flat array containing all results
|
54
|
+
def query_single(sql, *params)
|
55
|
+
result = run(sql, params)
|
56
|
+
result.type_map = type_map
|
57
|
+
if result.nfields == 1
|
58
|
+
result.column_values(0)
|
59
|
+
else
|
60
|
+
tuples = result.ntuples
|
61
|
+
fields = result.nfields
|
62
|
+
|
63
|
+
array = []
|
64
|
+
f = 0
|
65
|
+
row = 0
|
66
|
+
|
67
|
+
while row < tuples
|
68
|
+
while f < fields
|
69
|
+
array << result.getvalue(row, f)
|
70
|
+
f += 1
|
71
|
+
end
|
72
|
+
f = 0
|
73
|
+
row += 1
|
74
|
+
end
|
75
|
+
array
|
76
|
+
end
|
77
|
+
ensure
|
78
|
+
result.clear if result
|
79
|
+
end
|
80
|
+
|
81
|
+
def query(sql, *params)
|
82
|
+
result = run(sql, params)
|
83
|
+
result.type_map = type_map
|
84
|
+
@deserializer_cache.materialize(result)
|
85
|
+
ensure
|
86
|
+
result.clear if result
|
87
|
+
end
|
88
|
+
|
89
|
+
def exec(sql, *params)
|
90
|
+
result = run(sql, params)
|
91
|
+
result.cmd_tuples
|
92
|
+
ensure
|
93
|
+
result.clear if result
|
94
|
+
end
|
95
|
+
|
96
|
+
def query_hash(sql, *params)
|
97
|
+
result = run(sql, params)
|
98
|
+
result.type_map = type_map
|
99
|
+
result.to_a
|
100
|
+
ensure
|
101
|
+
result.clear if result
|
102
|
+
end
|
103
|
+
|
104
|
+
def build(sql)
|
105
|
+
Builder.new(self, sql)
|
106
|
+
end
|
107
|
+
|
108
|
+
def escape_string(str)
|
109
|
+
raw_connection.escape_string(str)
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def run(sql, params)
|
115
|
+
if params && params.length > 0
|
116
|
+
sql = param_encoder.encode(sql, *params)
|
117
|
+
end
|
118
|
+
raw_connection.async_exec(sql)
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module MiniSql
|
2
|
+
module Postgres
|
3
|
+
class DeserializerCache
|
4
|
+
|
5
|
+
DEFAULT_MAX_SIZE = 500
|
6
|
+
|
7
|
+
def initialize(max_size = nil)
|
8
|
+
@cache = {}
|
9
|
+
@max_size = max_size || DEFAULT_MAX_SIZE
|
10
|
+
end
|
11
|
+
|
12
|
+
def materialize(result)
|
13
|
+
|
14
|
+
return [] if result.ntuples == 0
|
15
|
+
|
16
|
+
key = result.fields
|
17
|
+
|
18
|
+
# trivial fast LRU implementation
|
19
|
+
materializer = @cache.delete(key)
|
20
|
+
if materializer
|
21
|
+
@cache[key] = materializer
|
22
|
+
else
|
23
|
+
materializer = @cache[key] = new_row_matrializer(result)
|
24
|
+
@cache.shift if @cache.length > @max_size
|
25
|
+
end
|
26
|
+
|
27
|
+
i = 0
|
28
|
+
r = []
|
29
|
+
# quicker loop
|
30
|
+
while i < result.ntuples
|
31
|
+
r << materializer.materialize(result, i)
|
32
|
+
i += 1
|
33
|
+
end
|
34
|
+
r
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def new_row_matrializer(result)
|
40
|
+
fields = result.fields
|
41
|
+
|
42
|
+
Class.new do
|
43
|
+
attr_accessor(*fields)
|
44
|
+
|
45
|
+
# AM serializer support
|
46
|
+
alias :read_attribute_for_serialization :send
|
47
|
+
|
48
|
+
def to_h
|
49
|
+
r = {}
|
50
|
+
instance_variables.each do |f|
|
51
|
+
r[f.to_s.sub('@','').to_sym] = instance_variable_get(f)
|
52
|
+
end
|
53
|
+
r
|
54
|
+
end
|
55
|
+
|
56
|
+
instance_eval <<~RUBY
|
57
|
+
def materialize(pg_result, index)
|
58
|
+
r = self.new
|
59
|
+
#{col=-1; fields.map{|f| "r.#{f} = pg_result.getvalue(index, #{col+=1})"}.join("; ")}
|
60
|
+
r
|
61
|
+
end
|
62
|
+
RUBY
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MiniSql
|
4
|
+
module Sqlite
|
5
|
+
class Connection < MiniSql::Connection
|
6
|
+
attr_reader :param_encoder, :raw_connection, :deserializer_cache
|
7
|
+
|
8
|
+
def initialize(raw_connection, args = nil)
|
9
|
+
@raw_connection = raw_connection
|
10
|
+
@param_encoder = (args && args[:param_encoder]) || InlineParamEncoder.new(self)
|
11
|
+
@deserializer_cache = (args && args[:deserializer_cache]) || DeserializerCache.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def query_single(sql, *params)
|
15
|
+
# a bit lazy can be optimized
|
16
|
+
run(sql, *params).flatten!
|
17
|
+
end
|
18
|
+
|
19
|
+
def query_hash(sql, *params)
|
20
|
+
r = []
|
21
|
+
run(sql, *params) do |set|
|
22
|
+
set.each_hash do |h|
|
23
|
+
r << h
|
24
|
+
end
|
25
|
+
end
|
26
|
+
r
|
27
|
+
end
|
28
|
+
|
29
|
+
def exec(sql, *params)
|
30
|
+
|
31
|
+
start = raw_connection.total_changes
|
32
|
+
|
33
|
+
r = run(sql, *params)
|
34
|
+
# this is not safe for multithreading, also for DELETE from TABLE will return
|
35
|
+
# incorrect data
|
36
|
+
if r.length > 0
|
37
|
+
r.length
|
38
|
+
else
|
39
|
+
raw_connection.total_changes - start
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def query(sql, *params)
|
44
|
+
run(sql, *params) do |set|
|
45
|
+
deserializer_cache.materialize(set)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def escape_string(str)
|
50
|
+
str.gsub("'","''")
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def run(sql, *params)
|
56
|
+
if params && params.length > 0
|
57
|
+
sql = param_encoder.encode(sql, *params)
|
58
|
+
end
|
59
|
+
if block_given?
|
60
|
+
stmt = SQLite3::Statement.new(raw_connection, sql)
|
61
|
+
yield stmt.execute
|
62
|
+
else
|
63
|
+
raw_connection.execute(sql)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module MiniSql
|
2
|
+
module Sqlite
|
3
|
+
class DeserializerCache
|
4
|
+
|
5
|
+
DEFAULT_MAX_SIZE = 500
|
6
|
+
|
7
|
+
def initialize(max_size = nil)
|
8
|
+
@cache = {}
|
9
|
+
@max_size = max_size || DEFAULT_MAX_SIZE
|
10
|
+
end
|
11
|
+
|
12
|
+
def materialize(result)
|
13
|
+
|
14
|
+
key = result.columns
|
15
|
+
|
16
|
+
# trivial fast LRU implementation
|
17
|
+
materializer = @cache.delete(key)
|
18
|
+
if materializer
|
19
|
+
@cache[key] = materializer
|
20
|
+
else
|
21
|
+
materializer = @cache[key] = new_row_matrializer(result)
|
22
|
+
@cache.shift if @cache.length > @max_size
|
23
|
+
end
|
24
|
+
|
25
|
+
r = []
|
26
|
+
# quicker loop
|
27
|
+
while !result.eof?
|
28
|
+
data = result.next
|
29
|
+
if data
|
30
|
+
r << materializer.materialize(data)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
r
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def new_row_matrializer(result)
|
39
|
+
fields = result.columns
|
40
|
+
|
41
|
+
Class.new do
|
42
|
+
attr_accessor(*fields)
|
43
|
+
|
44
|
+
# AM serializer support
|
45
|
+
alias :read_attribute_for_serialization :send
|
46
|
+
|
47
|
+
def to_h
|
48
|
+
r = {}
|
49
|
+
instance_variables.each do |f|
|
50
|
+
r[f.to_s.sub('@','').to_sym] = instance_variable_get(f)
|
51
|
+
end
|
52
|
+
r
|
53
|
+
end
|
54
|
+
|
55
|
+
instance_eval <<~RUBY
|
56
|
+
def materialize(data)
|
57
|
+
r = self.new
|
58
|
+
#{col=-1; fields.map{|f| "r.#{f} = data[#{col+=1}]"}.join("; ")}
|
59
|
+
r
|
60
|
+
end
|
61
|
+
RUBY
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/lib/mini_sql/version.rb
CHANGED
data/mini_sql.gemspec
CHANGED
@@ -22,11 +22,12 @@ Gem::Specification.new do |spec|
|
|
22
22
|
end
|
23
23
|
spec.require_paths = ["lib"]
|
24
24
|
|
25
|
-
spec.add_development_dependency "bundler", "
|
25
|
+
spec.add_development_dependency "bundler", "> 1.16"
|
26
26
|
spec.add_development_dependency "rake", "~> 10.0"
|
27
27
|
spec.add_development_dependency "minitest", "~> 5.0"
|
28
|
-
spec.add_development_dependency "pg", "
|
28
|
+
spec.add_development_dependency "pg", "> 1"
|
29
29
|
spec.add_development_dependency "guard", "~> 2.14"
|
30
30
|
spec.add_development_dependency "guard-minitest", "~> 2.4"
|
31
31
|
spec.add_development_dependency "activesupport", "~> 5.2"
|
32
|
+
spec.add_development_dependency "sqlite3", "~> 1.3"
|
32
33
|
end
|
metadata
CHANGED
@@ -1,27 +1,27 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mini_sql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Saffron
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-02-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '1.16'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.16'
|
27
27
|
- !ruby/object:Gem::Dependency
|
@@ -56,16 +56,16 @@ dependencies:
|
|
56
56
|
name: pg
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - "
|
59
|
+
- - ">"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 1
|
61
|
+
version: '1'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - "
|
66
|
+
- - ">"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 1
|
68
|
+
version: '1'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: guard
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -108,6 +108,20 @@ dependencies:
|
|
108
108
|
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '5.2'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: sqlite3
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '1.3'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '1.3'
|
111
125
|
description: A fast, safe, simple direct SQL executor for PG
|
112
126
|
email:
|
113
127
|
- sam.saffron@gmail.com
|
@@ -117,6 +131,7 @@ extra_rdoc_files: []
|
|
117
131
|
files:
|
118
132
|
- ".gitignore"
|
119
133
|
- ".travis.yml"
|
134
|
+
- CHANGELOG
|
120
135
|
- CODE_OF_CONDUCT.md
|
121
136
|
- Gemfile
|
122
137
|
- Guardfile
|
@@ -129,10 +144,14 @@ files:
|
|
129
144
|
- bin/setup
|
130
145
|
- lib/mini_sql.rb
|
131
146
|
- lib/mini_sql/builder.rb
|
132
|
-
- lib/mini_sql/coders.rb
|
133
147
|
- lib/mini_sql/connection.rb
|
134
148
|
- lib/mini_sql/deserializer_cache.rb
|
135
149
|
- lib/mini_sql/inline_param_encoder.rb
|
150
|
+
- lib/mini_sql/postgres/coders.rb
|
151
|
+
- lib/mini_sql/postgres/connection.rb
|
152
|
+
- lib/mini_sql/postgres/deserializer_cache.rb
|
153
|
+
- lib/mini_sql/sqlite/connection.rb
|
154
|
+
- lib/mini_sql/sqlite/deserializer_cache.rb
|
136
155
|
- lib/mini_sql/version.rb
|
137
156
|
- mini_sql.gemspec
|
138
157
|
homepage: https://discourse.org
|
@@ -154,8 +173,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
154
173
|
- !ruby/object:Gem::Version
|
155
174
|
version: '0'
|
156
175
|
requirements: []
|
157
|
-
|
158
|
-
rubygems_version: 2.7.6
|
176
|
+
rubygems_version: 3.0.1
|
159
177
|
signing_key:
|
160
178
|
specification_version: 4
|
161
179
|
summary: A fast, safe, simple direct SQL executor
|
data/lib/mini_sql/coders.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
module MiniSql
|
2
|
-
module Coders
|
3
|
-
class NumericCoder < PG::SimpleDecoder
|
4
|
-
def decode(string, tuple = nil, field = nil)
|
5
|
-
BigDecimal.new(string)
|
6
|
-
end
|
7
|
-
end
|
8
|
-
|
9
|
-
class IPAddrCoder < PG::SimpleDecoder
|
10
|
-
def decode(string, tuple = nil, field = nil)
|
11
|
-
IPAddr.new(string)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
class TimestampUtc < PG::SimpleDecoder
|
16
|
-
# exact same implementation as Rails here
|
17
|
-
ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/
|
18
|
-
|
19
|
-
def decode(string, tuple = nil, field = nil)
|
20
|
-
if string =~ ISO_DATETIME
|
21
|
-
microsec = ($7.to_r * 1_000_000).to_i
|
22
|
-
Time.utc $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec
|
23
|
-
else
|
24
|
-
STDERR.puts "unexpected date time format #{string}"
|
25
|
-
string
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
end
|
31
|
-
end
|