mini_sql 0.1.10 → 0.2.1

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