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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c66544eb3a8d9b5067dc3ba1fb1df6a542e4adeabc2f4b08d4392fab7955b382
4
- data.tar.gz: c3701417f3667ae360470d24a2bdd60b002350a1690a28cb1e9c95b0040d9c96
3
+ metadata.gz: 8b9e4f7f96a77f3eaead87f509583b9bf604d49eec6e3caaf8a0234646eb208a
4
+ data.tar.gz: 94cac1f8ef8a82860686b1001761ef1b06a8be5228299427dc41d6be92d5a1c5
5
5
  SHA512:
6
- metadata.gz: d8b474bc10bd5570a2b4932f5182e97ad5c242885d2e3e68e8fa5246d21bf908b57044daf107b1968cf4895cb0e32af1af05f649c6049cd0747ddd43d2647836
7
- data.tar.gz: 9c98da1a59f650541955b435f34fdbc3d4ed2ee7245d254d6d7decf9edbb25c8436b98a0dd0f3f4017e2f003ee51ae5f8f38e294c74f826be160d661bd361bac
6
+ metadata.gz: 04a54bb044404c6ed17bd425b0f7779afa130002850592ad1e91a249504c0b4b46e17453a59dab0e5e816a131669079742ca1baaae013259917bfec6dedab83f
7
+ data.tar.gz: fd4ceaf5f7e46efb8e5b067a54304c5a5eaa5d4216ff4c405bb64688b57b7844d7106dc3568e059b912994b193e197b83f9e81bfb1d5e91611b8c3ce5b65033f
@@ -0,0 +1,3 @@
1
+ 2019-02-25 - 0.2.1
2
+
3
+ - Handle `BigDecimal.new` deprecation by using `BigDecimal()` instead
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.new(pg_conn)
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.new(pg_conn)
82
+ conn = MiniSql::Connection.get(pg_conn)
83
83
  r = conn.query('select * from table')
84
84
 
85
85
  r.each do |row|
@@ -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.iso8601
78
- s << time2.iso8601
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.iso8601
87
- s << t.time2.iso8601
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 pg_times(l=1000)
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
- r.each do |row|
100
- s << row["time1"].iso8601
101
- s << row["time2"].iso8601
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.iso8601
112
- s << t.time2.iso8601
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.iso8601
121
- s << t.time2.iso8601
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].iso8601
130
- s << t[1].iso8601
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].iso8601
142
- s << r[i+1].iso8601
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
- s = ""
152
- r = $swift.execute("select time1, time2 from timestamps order by id limit $1", l)
153
- r.each do |row|
154
- s << row[:time1].iso8601
155
- s << row[:time2].iso8601
156
- end
157
- s
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.report("ar select times") do |n|
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
- ar_select_times
216
+ mini_sql_times_single
177
217
  n -= 1
178
218
  end
179
219
  end
180
- r.report("ar pluck times") do |n|
220
+ r.report("sequel times") do |n|
181
221
  while n > 0
182
- ar_pluck_times
222
+ sequel_times
183
223
  n -= 1
184
224
  end
185
225
  end
186
- r.report("sequel times") do |n|
226
+ r.report("pg times async_exec values") do |n|
187
227
  while n > 0
188
- sequel_times
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("mini_sql query_single times") do |n|
250
+ r.report("sequel raw times") do |n|
211
251
  while n > 0
212
- mini_sql_times_single
252
+ sequel_raw_times
213
253
  n -= 1
214
254
  end
215
255
  end
216
- r.report("swift_select_times") do |n|
256
+ r.report("ar select times") do |n|
217
257
  while n > 0
218
- swift_select_times
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
- # Comparison:
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
- # if we force it we get:
238
- #
239
- # swift_select_times: 222.7 i/s
240
- # mini_sql query_single times: 48.4 i/s - 4.60x slower
241
- # mini sql times: 46.4 i/s - 4.80x slower
242
- # pg times: 44.2 i/s - 5.03x slower
243
- # ar pluck times: 32.5 i/s - 6.85x slower
244
- # ar select times: 22.1 i/s - 10.06x slower
245
- # sequel pluck times: 10.9 i/s - 20.50x slower
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
- # swift has a super fast implementation, still need to determine
249
- # why pg is so far behind
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
@@ -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
- r.each do |row|
135
- s << row["id"].to_s
136
- s << row["title"]
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("wide topic ar") do |n|
223
+ r.report("ar select title id") do |n|
218
224
  while n > 0
219
- wide_topic_ar
225
+ ar_title_id
220
226
  n -= 1
221
227
  end
222
228
  end
223
- r.report("wide topic sequel") do |n|
229
+ r.report("ar select title id pluck") do |n|
224
230
  while n > 0
225
- wide_topic_sequel
231
+ ar_title_id_pluck
226
232
  n -= 1
227
233
  end
228
234
  end
229
- r.report("wide topic pg") do |n|
235
+ r.report("sequel title id select") do |n|
230
236
  while n > 0
231
- wide_topic_pg
237
+ sequel_select_title_id
232
238
  n -= 1
233
239
  end
234
240
  end
235
- r.report("wide topic mini sql") do |n|
241
+ r.report("pg select title id") do |n|
236
242
  while n > 0
237
- wide_topic_mini_sql
243
+ pg_title_id
238
244
  n -= 1
239
245
  end
240
246
  end
241
- r.compare!
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
- ar_title_id
249
+ mini_sql_title_id
250
250
  n -= 1
251
251
  end
252
252
  end
253
- r.report("ar select title id pluck") do |n|
253
+ r.report("sequel title id pluck") do |n|
254
254
  while n > 0
255
- ar_title_id_pluck
255
+ sequel_pluck_title_id
256
256
  n -= 1
257
257
  end
258
258
  end
259
- r.report("sequel title id select") do |n|
259
+ r.report("mini_sql query_single title id") do |n|
260
260
  while n > 0
261
- sequel_select_title_id
261
+ mini_sql_title_id_query_single
262
262
  n -= 1
263
263
  end
264
264
  end
265
- r.report("pg select title id") do |n|
265
+ r.report("swift title id") do |n|
266
266
  while n > 0
267
- pg_title_id
267
+ swift_select_title_id
268
268
  n -= 1
269
269
  end
270
270
  end
271
- r.report("mini_sql select title id") do |n|
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
- mini_sql_title_id
298
+ wide_topic_ar
274
299
  n -= 1
275
300
  end
276
301
  end
277
- r.report("sequel title id pluck") do |n|
302
+ r.report("wide topic sequel") do |n|
278
303
  while n > 0
279
- sequel_pluck_title_id
304
+ wide_topic_sequel
280
305
  n -= 1
281
306
  end
282
307
  end
283
- r.report("mini_sql query_single title id") do |n|
308
+ r.report("wide topic pg") do |n|
284
309
  while n > 0
285
- mini_sql_title_id_query_single
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
- # wide topic mini sql: 7535.8 i/s
301
- # wide topic pg: 7345.1 i/s - same-ish: difference falls within error
302
- # wide topic sequel: 3449.4 i/s - 2.18x slower
303
- # wide topic ar: 2382.9 i/s - 3.16x slower
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
- # sequel title id pluck: 1181.0 i/s
321
- # mini_sql query_single title id: 1171.1 i/s - same-ish: difference falls within error
322
- # mini_sql select title id: 1105.6 i/s - 1.07x slower
323
- # pg select title id: 1002.2 i/s - 1.18x slower
324
- # sequel title id select: 916.8 i/s - 1.29x slower
325
- # ar select title id pluck: 696.2 i/s - 1.70x slower
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
@@ -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
- autoload :Coders, "mini_sql/coders"
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
@@ -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.default_deserializer_cache
8
- @deserializer_cache ||= DeserializerCache.new
9
- end
10
-
11
- def self.type_map(conn)
12
- @type_map ||=
13
- begin
14
- map = PG::BasicTypeMapForResults.new(conn)
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
- result = run(sql, params)
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
- result = run(sql, params)
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
- result = run(sql, params)
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
- result = run(sql, params)
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
- raw_connection.escape_string(str)
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
@@ -1,3 +1,3 @@
1
1
  module MiniSql
2
- VERSION = "0.1.10"
2
+ VERSION = "0.2.1"
3
3
  end
@@ -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", "~> 1.16"
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", "~> 1.0.0"
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.10
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: 2018-06-24 00:00:00.000000000 Z
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.0.0
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.0.0
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
- rubyforge_project:
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
@@ -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