mini_sql 0.2.2 → 1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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