mini_sql 0.2.2 → 1.0

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.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bundler/inline'
2
4
 
3
5
  gemfile do
@@ -12,6 +14,7 @@ gemfile do
12
14
  gem 'sequel', github: 'jeremyevans/sequel'
13
15
  gem 'sequel_pg', github: 'jeremyevans/sequel_pg', require: 'sequel'
14
16
  gem 'swift-db-postgres', github: 'deepfryed/swift-db-postgres'
17
+ gem 'draper'
15
18
  end
16
19
 
17
20
  require 'sequel'
@@ -89,7 +92,6 @@ end
89
92
  class TopicSequel < Sequel::Model(:topics)
90
93
  end
91
94
 
92
-
93
95
  Topic.transaction do
94
96
  topic = {
95
97
  }
@@ -148,7 +150,7 @@ def pg_title_id
148
150
  s
149
151
  end
150
152
 
151
- $mini_sql = MiniSql::Connection.new($conn)
153
+ $mini_sql = MiniSql::Connection.get($conn)
152
154
 
153
155
  def mini_sql_title_id
154
156
  s = +""
@@ -184,7 +186,7 @@ def mini_sql_title_id_query_single
184
186
  r = $mini_sql.query_single(-"select id, title from topics order by id limit 1000")
185
187
  while i < r.length
186
188
  s << r[i].to_s
187
- s << r[i+1]
189
+ s << r[i + 1]
188
190
  i += 2
189
191
  end
190
192
  s
@@ -193,8 +195,8 @@ end
193
195
  # connects over unix socket
194
196
  $swift = Swift::DB::Postgres.new(db: "test_db")
195
197
 
196
- def swift_select_title_id(l=1000)
197
- s = ""
198
+ def swift_select_title_id(l = 1000)
199
+ s = +''
198
200
  i = 0
199
201
  r = $swift.execute("select id, title from topics order by id limit 1000")
200
202
  while i < r.selected_rows
@@ -218,6 +220,174 @@ results = [
218
220
 
219
221
  exit(-1) unless results.uniq.length == 1
220
222
 
223
+ # https://github.com/drapergem/draper
224
+ class TopicDraper < Draper::Decorator
225
+ delegate :id
226
+
227
+ def title_bang
228
+ object.title + '!!!'
229
+ end
230
+ end
231
+
232
+ # https://ruby-doc.org/stdlib-2.5.1/libdoc/delegate/rdoc/SimpleDelegator.html
233
+ class TopicSimpleDelegator < SimpleDelegator
234
+ def title_bang
235
+ title + '!!!'
236
+ end
237
+ end
238
+
239
+ class TopicDecoratorSequel < TopicSequel
240
+ def title_bang
241
+ title + '!!!'
242
+ end
243
+ end
244
+
245
+ class TopicArModel < Topic
246
+ def title_bang
247
+ title + '!!!'
248
+ end
249
+ end
250
+
251
+ module TopicDecorator
252
+ def title_bang
253
+ title + '!!!'
254
+ end
255
+ end
256
+
257
+ Benchmark.ips do |r|
258
+ r.report('query_decorator') do |n|
259
+ while n > 0
260
+ $mini_sql.query_decorator(TopicDecorator, 'select id, title from topics order by id limit 1000').each do |obj|
261
+ obj.title_bang
262
+ obj.id
263
+ end
264
+ n -= 1
265
+ end
266
+ end
267
+ r.report('extend') do |n|
268
+ while n > 0
269
+ $mini_sql.query('select id, title from topics order by id limit 1000').each do |obj|
270
+ d_obj = obj.extend(TopicDecorator)
271
+ d_obj.title_bang
272
+ d_obj.id
273
+ end
274
+ n -= 1
275
+ end
276
+ end
277
+ r.report('draper') do |n|
278
+ while n > 0
279
+ $mini_sql.query('select id, title from topics order by id limit 1000').each do |obj|
280
+ d_obj = TopicDraper.new(obj)
281
+ d_obj.title_bang
282
+ d_obj.id
283
+ end
284
+ n -= 1
285
+ end
286
+ end
287
+ r.report('simple_delegator') do |n|
288
+ while n > 0
289
+ $mini_sql.query('select id, title from topics order by id limit 1000').each do |obj|
290
+ d_obj = TopicSimpleDelegator.new(obj)
291
+ d_obj.title_bang
292
+ d_obj.id
293
+ end
294
+ n -= 1
295
+ end
296
+ end
297
+ r.report('query') do |n|
298
+ while n > 0
299
+ $mini_sql.query('select id, title from topics order by id limit 1000').each do |obj|
300
+ obj.title + '!!!'
301
+ obj.id
302
+ end
303
+ n -= 1
304
+ end
305
+ end
306
+ r.report('ar model') do |n|
307
+ while n > 0
308
+ TopicArModel.limit(1000).order(:id).select(:id, :title).each do |obj|
309
+ obj.title_bang
310
+ obj.id
311
+ end
312
+ n -= 1
313
+ end
314
+ end
315
+ r.report('sequel model') do |n|
316
+ while n > 0
317
+ TopicDecoratorSequel.limit(1000).order(:id).select(:id, :title).each do |obj|
318
+ obj.title_bang
319
+ obj.id
320
+ end
321
+ n -= 1
322
+ end
323
+ end
324
+
325
+ r.compare!
326
+ end
327
+
328
+ # Comparison:
329
+ # query: 828.4 i/s
330
+ # query_decorator: 819.3 i/s - same-ish: difference falls within error
331
+ # sequel model: 672.4 i/s - 1.23x slower
332
+ # extend: 519.4 i/s - 1.59x slower
333
+ # simple_delegator: 496.8 i/s - 1.67x slower
334
+ # draper: 416.2 i/s - 1.99x slower
335
+ # ar model: 113.4 i/s - 7.30x slower
336
+
337
+ Benchmark.ips do |r|
338
+ r.report('query_hash') do |n|
339
+ while n > 0
340
+ $mini_sql.query_hash('select id, title from topics order by id limit 1000').each do |hash|
341
+ [hash['id'], hash['title']]
342
+ end
343
+ n -= 1
344
+ end
345
+ end
346
+ r.report('query_array') do |n|
347
+ while n > 0
348
+ $mini_sql.query_array('select id, title from topics order by id limit 1000').each do |id, title|
349
+ [id, title]
350
+ end
351
+ n -= 1
352
+ end
353
+ end
354
+ r.report('query') do |n|
355
+ while n > 0
356
+ $mini_sql.query('select id, title from topics order by id limit 1000').each do |obj|
357
+ [obj.id, obj.title]
358
+ end
359
+ n -= 1
360
+ end
361
+ end
362
+
363
+ r.compare!
364
+ end
365
+
366
+ # Comparison:
367
+ # query_array: 1351.6 i/s
368
+ # query: 963.8 i/s - 1.40x slower
369
+ # query_hash: 787.4 i/s - 1.72x slower
370
+
371
+ Benchmark.ips do |r|
372
+ r.report('query_single') do |n|
373
+ while n > 0
374
+ $mini_sql.query_single('select id from topics order by id limit 1000')
375
+ n -= 1
376
+ end
377
+ end
378
+ r.report('query_array') do |n|
379
+ while n > 0
380
+ $mini_sql.query_array('select id from topics order by id limit 1000').flatten
381
+ n -= 1
382
+ end
383
+ end
384
+
385
+ r.compare!
386
+ end
387
+
388
+ # Comparison:
389
+ # query_single: 2368.9 i/s
390
+ # query_array: 1350.1 i/s - 1.75x slower
221
391
 
222
392
  Benchmark.ips do |r|
223
393
  r.report("ar select title id") do |n|
@@ -271,8 +441,6 @@ Benchmark.ips do |r|
271
441
  r.compare!
272
442
  end
273
443
 
274
-
275
-
276
444
  def wide_topic_ar
277
445
  Topic.first
278
446
  end
@@ -320,7 +488,6 @@ Benchmark.ips do |r|
320
488
  r.compare!
321
489
  end
322
490
 
323
-
324
491
  # Comparison:
325
492
  # pg select title id: 1519.7 i/s
326
493
  # mini_sql query_single title id: 1335.0 i/s - 1.14x slower
@@ -338,10 +505,7 @@ end
338
505
  # wide topic sequel: 5696.8 i/s - 1.31x slower
339
506
  # wide topic ar: 2515.0 i/s - 2.97x slower
340
507
 
341
-
342
-
343
508
  # to run deep analysis run
344
509
  # MemoryProfiler.report do
345
510
  # ar
346
511
  # end.pretty_print
347
-
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require "bundler/setup"
4
5
  require "mini_sql"
@@ -8,6 +8,9 @@ require_relative "mini_sql/connection"
8
8
  require_relative "mini_sql/deserializer_cache"
9
9
  require_relative "mini_sql/builder"
10
10
  require_relative "mini_sql/inline_param_encoder"
11
+ require_relative "mini_sql/decoratable"
12
+ require_relative "mini_sql/serializer"
13
+ require_relative "mini_sql/result"
11
14
 
12
15
  module MiniSql
13
16
  if RUBY_ENGINE == 'jruby'
@@ -26,5 +29,10 @@ module MiniSql
26
29
  autoload :Connection, "mini_sql/sqlite/connection"
27
30
  autoload :DeserializerCache, "mini_sql/sqlite/deserializer_cache"
28
31
  end
32
+
33
+ module Mysql
34
+ autoload :Connection, "mini_sql/mysql/connection"
35
+ autoload :DeserializerCache, "mini_sql/mysql/deserializer_cache"
36
+ end
29
37
  end
30
38
  end
@@ -57,7 +57,6 @@ class MiniSql::Builder
57
57
  def #{m}(hash_args = nil)
58
58
  hash_args = @args.merge(hash_args) if hash_args && @args
59
59
  hash_args ||= @args
60
-
61
60
  if hash_args
62
61
  @connection.#{m}(to_sql, hash_args)
63
62
  else
@@ -67,5 +66,14 @@ class MiniSql::Builder
67
66
  RUBY
68
67
  end
69
68
 
70
- end
69
+ def query_decorator(decorator, hash_args = nil)
70
+ hash_args = @args.merge(hash_args) if hash_args && @args
71
+ hash_args ||= @args
72
+ if hash_args
73
+ @connection.query_decorator(decorator, to_sql, hash_args)
74
+ else
75
+ @connection.query_decorator(decorator, to_sql)
76
+ end
77
+ end
71
78
 
79
+ end
@@ -4,12 +4,14 @@ module MiniSql
4
4
  class Connection
5
5
 
6
6
  def self.get(raw_connection, options = {})
7
- if (defined? ::PG::Connection) && (PG::Connection === raw_connection)
7
+ if (defined? ::PG::Connection) && (PG::Connection === raw_connection)
8
8
  Postgres::Connection.new(raw_connection, options)
9
9
  elsif (defined? ::ArJdbc)
10
10
  Postgres::Connection.new(raw_connection, options)
11
11
  elsif (defined? ::SQLite3::Database) && (SQLite3::Database === raw_connection)
12
12
  Sqlite::Connection.new(raw_connection, options)
13
+ elsif (defined? ::Mysql2::Client) && (Mysql2::Client === raw_connection)
14
+ Mysql::Connection.new(raw_connection, options)
13
15
  else
14
16
  raise ArgumentError, 'unknown connection type!'
15
17
  end
@@ -29,11 +31,23 @@ module MiniSql
29
31
  raise NotImplementedError, "must be implemented by child connection"
30
32
  end
31
33
 
32
- def exec(sql, *params)
34
+ def query_hash(sql, *params)
33
35
  raise NotImplementedError, "must be implemented by child connection"
34
36
  end
35
37
 
36
- def query_hash(sql, *params)
38
+ def query_decorator(sql, *params)
39
+ raise NotImplementedError, "must be implemented by child connection"
40
+ end
41
+
42
+ def query_each(sql, *params)
43
+ raise NotImplementedError, "must be implemented by child connection"
44
+ end
45
+
46
+ def query_each_hash(sql, *params)
47
+ raise NotImplementedError, "must be implemented by child connection"
48
+ end
49
+
50
+ def exec(sql, *params)
37
51
  raise NotImplementedError, "must be implemented by child connection"
38
52
  end
39
53
 
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MiniSql
4
+ module Decoratable
5
+ def decorated(mod)
6
+ @decoratorated_classes ||= {}
7
+ @decoratorated_classes[mod] ||=
8
+ Class.new(self) do
9
+ include(mod)
10
+ instance_eval <<~RUBY
11
+ def decorator
12
+ #{mod}
13
+ end
14
+ RUBY
15
+ end
16
+ end
17
+
18
+ def decorator
19
+ nil
20
+ end
21
+ end
22
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module MiniSql
2
4
  class DeserializerCache
3
5
  # method takes a raw result and converts to proper objects
@@ -50,19 +50,19 @@ module MiniSql
50
50
 
51
51
  def quote_val(value)
52
52
  case value
53
+ when String then "'#{conn.escape_string(value.to_s)}'"
54
+ when Numeric then value.to_s
55
+ when BigDecimal then value.to_s("F")
56
+ when Date, Time then "'#{quoted_date(value)}'"
57
+ when Symbol then "'#{conn.escape_string(value.to_s)}'"
58
+ when true then "true"
59
+ when false then "false"
60
+ when nil then "NULL"
61
+ when [] then "NULL"
53
62
  when Array
54
63
  value.map do |v|
55
64
  quote_val(v)
56
65
  end.join(', ')
57
- when String
58
- "'#{conn.escape_string(value.to_s)}'"
59
- when true then "true"
60
- when false then "false"
61
- when nil then "NULL"
62
- when BigDecimal then value.to_s("F")
63
- when Numeric then value.to_s
64
- when Date, Time then "'#{quoted_date(value)}'"
65
- when Symbol then "'#{escape_string(value.to_s)}'"
66
66
  else raise TypeError, "can't quote #{value.class.name}"
67
67
  end
68
68
  end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MiniSql
4
+ module Mysql
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
+ run(sql, :array, params).to_a.flatten!
16
+ end
17
+
18
+ def query_hash(sql, *params)
19
+ result = run(sql, :hash, params)
20
+ result.to_a
21
+ end
22
+
23
+ def query_array(sql, *params)
24
+ run(sql, :array, params).to_a
25
+ end
26
+
27
+ def exec(sql, *params)
28
+ run(sql, :array, params)
29
+ raw_connection.affected_rows
30
+ end
31
+
32
+ def query(sql, *params)
33
+ result = run(sql, :array, params)
34
+ @deserializer_cache.materialize(result)
35
+ end
36
+
37
+ def query_decorator(decorator, sql, *params)
38
+ result = run(sql, :array, params)
39
+ @deserializer_cache.materialize(result, decorator)
40
+ end
41
+
42
+ def escape_string(str)
43
+ raw_connection.escape(str)
44
+ end
45
+
46
+ def build(sql)
47
+ Builder.new(self, sql)
48
+ end
49
+
50
+ private
51
+
52
+ def run(sql, as, params)
53
+ sql = param_encoder.encode(sql, *params)
54
+ raw_connection.query(
55
+ sql,
56
+ as: as,
57
+ database_timezone: :utc,
58
+ application_timezone: :utc,
59
+ cast_booleans: true,
60
+ cast: true,
61
+ cache_rows: true,
62
+ symbolize_keys: false
63
+ )
64
+ end
65
+ end
66
+ end
67
+ end