mini_sql 0.2.2-java → 0.2.3-java

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: 2fa76e26bf57648741cebb8a9ddbc55dddb48b51b10ac4a5ecf0f8ac7cf58373
4
- data.tar.gz: 673660673dbf917c3eaa48c13a73ca2e0fa1fd58972705ef10778b826c09fcd6
3
+ metadata.gz: bd0b01050fe87946f8e56c8069c6884f9509176b86ba16968a96c3c2aeac5470
4
+ data.tar.gz: 72efc80be8e29a5dd211512c4cab2e924087c7257602032cec70720fcbf280db
5
5
  SHA512:
6
- metadata.gz: f7de03a4f91d31b466c3b3f7c7fc25b7cdc2b35229bb36e921a8258088fc1035dfd116c29db0cae1e2aa887eb31ae411c19a17ed8d0b2e27e301b80752e88599
7
- data.tar.gz: 7bf2cde4021d60268608cfb4b144af908a0993a044af06e43ea1400c6dba34cb9c392b28cdd8cde4758a88bcab2d83f59df46ffbd5e52aad94db76b3d3ed1377
6
+ metadata.gz: 2d65d2cabe858dfdc634dfe27824987a73031ba1bc15b38bf4399948bfea4297916be903ddd08e773d54e18d4f44fa8c3ca32a9ae5e4c2e5cd8ee8c70ee8402b
7
+ data.tar.gz: 162f6c900fcaf39beaf0db5c463ab992d426ebc22fd202b05adb541616e3f94e16d0287ac5366c2656789c6c2c0b29fc7b4e36cd2f34e5cdab592fcde7ed7527
@@ -9,11 +9,16 @@ before_install:
9
9
  cache: bundler
10
10
  sudo: false
11
11
 
12
+ services:
13
+ - mysql
14
+
12
15
  addons:
13
16
  postgresql: 9.6
17
+ mysql: 5.7
14
18
 
15
19
  install:
16
20
  - createdb test_mini_sql
21
+ - mysql -e 'CREATE DATABASE test_mini_sql;'
17
22
  - bundle install
18
23
 
19
24
  matrix:
data/CHANGELOG CHANGED
@@ -1,3 +1,7 @@
1
+ 2019-12-20 - 0.2.3
2
+
3
+ - Added support for MySQL
4
+
1
5
  2019-11-04 - 0.2.2
2
6
 
3
7
  - Added adapters for JRuby postgres support thanks to @enebo
data/README.md CHANGED
@@ -109,7 +109,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
109
109
 
110
110
  ## Contributing
111
111
 
112
- Bug reports and pull requests are welcome on GitHub at https://github.com/SamSaffron/mini_sql. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
112
+ Bug reports and pull requests are welcome on GitHub at https://github.com/discourse/mini_sql. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
113
113
 
114
114
  ## License
115
115
 
@@ -0,0 +1,310 @@
1
+ require 'bundler/inline'
2
+
3
+ gemfile do
4
+ source 'https://rubygems.org'
5
+ gem 'mysql2'
6
+ gem 'mini_sql', path: '../'
7
+ gem 'activesupport'
8
+ gem 'activerecord'
9
+ gem 'activemodel'
10
+ gem 'memory_profiler'
11
+ gem 'benchmark-ips'
12
+ gem 'sequel', github: 'jeremyevans/sequel'
13
+ end
14
+
15
+ require 'mysql2'
16
+ require 'sequel'
17
+ require 'active_record'
18
+ require 'memory_profiler'
19
+ require 'benchmark/ips'
20
+ require 'mini_sql'
21
+
22
+ ActiveRecord::Base.establish_connection(
23
+ :adapter => "mysql2",
24
+ :database => "test_db",
25
+ :username => "root",
26
+ :password => ''
27
+ )
28
+
29
+ DB = Sequel.connect("mysql2://root:@localhost/test_db")
30
+
31
+ mysql = ActiveRecord::Base.connection.raw_connection
32
+
33
+ mysql.query <<SQL
34
+ drop table if exists topics
35
+ SQL
36
+
37
+ mysql.query <<~SQL
38
+ CREATE TABLE `topics` (
39
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
40
+ `title` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
41
+ `last_posted_at` datetime DEFAULT NULL,
42
+ `created_at` datetime NOT NULL,
43
+ `updated_at` datetime NOT NULL,
44
+ `views` int(11) NOT NULL DEFAULT '0',
45
+ `posts_count` int(11) NOT NULL DEFAULT '0',
46
+ `user_id` int(11) DEFAULT NULL,
47
+ `last_post_user_id` int(11) NOT NULL,
48
+ `reply_count` int(11) NOT NULL DEFAULT '0',
49
+ `featured_user1_id` int(11) DEFAULT NULL,
50
+ `featured_user2_id` int(11) DEFAULT NULL,
51
+ `featured_user3_id` int(11) DEFAULT NULL,
52
+ `avg_time` int(11) DEFAULT NULL,
53
+ `deleted_at` datetime DEFAULT NULL,
54
+ `highest_post_number` int(11) NOT NULL DEFAULT '0',
55
+ `image_url` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
56
+ `like_count` int(11) NOT NULL DEFAULT '0',
57
+ `incoming_link_count` int(11) NOT NULL DEFAULT '0',
58
+ `category_id` int(11) DEFAULT NULL,
59
+ `visible` tinyint(1) NOT NULL DEFAULT '1',
60
+ `moderator_posts_count` int(11) NOT NULL DEFAULT '0',
61
+ `closed` tinyint(1) NOT NULL DEFAULT '0',
62
+ `archived` tinyint(1) NOT NULL DEFAULT '0',
63
+ `bumped_at` datetime NOT NULL,
64
+ `has_summary` tinyint(1) NOT NULL DEFAULT '0',
65
+ `archetype` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'regular',
66
+ `featured_user4_id` int(11) DEFAULT NULL,
67
+ `notify_moderators_count` int(11) NOT NULL DEFAULT '0',
68
+ `spam_count` int(11) NOT NULL DEFAULT '0',
69
+ `pinned_at` datetime DEFAULT NULL,
70
+ `score` float DEFAULT NULL,
71
+ `percent_rank` float NOT NULL DEFAULT '1',
72
+ `subtype` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
73
+ `slug` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
74
+ `deleted_by_id` int(11) DEFAULT NULL,
75
+ `participant_count` int(11) DEFAULT '1',
76
+ `word_count` int(11) DEFAULT NULL,
77
+ `excerpt` varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
78
+ `pinned_globally` tinyint(1) NOT NULL DEFAULT '0',
79
+ `pinned_until` datetime DEFAULT NULL,
80
+ `fancy_title` varchar(400) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
81
+ `highest_staff_post_number` int(11) NOT NULL DEFAULT '0',
82
+ `featured_link` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
83
+ `reviewable_score` float NOT NULL DEFAULT '0',
84
+ PRIMARY KEY (`id`)
85
+ )
86
+ SQL
87
+
88
+ class Topic < ActiveRecord::Base
89
+ end
90
+
91
+ class TopicSequel < Sequel::Model(:topics)
92
+ end
93
+
94
+
95
+ Topic.transaction do
96
+ topic = {
97
+ }
98
+ Topic.columns.each do |c|
99
+ topic[c.name.to_sym] = case c.type
100
+ when :integer then 1
101
+ when :datetime then Time.now
102
+ when :boolean then false
103
+ when :float then 1.0
104
+ else "HELLO WORLD" * 2
105
+ end
106
+ end
107
+
108
+ 1000.times do |id|
109
+ topic[:id] = id
110
+ Topic.create!(topic)
111
+ end
112
+ end
113
+
114
+ $conn = ActiveRecord::Base.connection.raw_connection
115
+
116
+ def ar_title_id_pluck
117
+ s = +""
118
+ Topic.limit(1000).order(:id).pluck(:id, :title).each do |id, title|
119
+ s << id.to_s
120
+ s << title
121
+ end
122
+ s
123
+ end
124
+
125
+ def ar_title_id
126
+ s = +""
127
+ Topic.limit(1000).order(:id).select(:id, :title).each do |t|
128
+ s << t.id.to_s
129
+ s << t.title
130
+ end
131
+ s
132
+ end
133
+
134
+ def mysql_title_id
135
+ s = +""
136
+ # use the safe pattern here
137
+ r = $conn.query(-"select id, title from topics order by id limit 1000", as: :array)
138
+
139
+ r.each do |row|
140
+ s << row[0].to_s
141
+ s << row[1]
142
+ end
143
+ s
144
+ end
145
+
146
+ $mini_sql = MiniSql::Connection.get($conn)
147
+
148
+ def mini_sql_title_id
149
+ s = +""
150
+ $mini_sql.query(-"select id, title from topics order by id limit 1000").each do |t|
151
+ s << t.id.to_s
152
+ s << t.title
153
+ end
154
+ s
155
+ end
156
+
157
+ def sequel_select_title_id
158
+ s = +""
159
+ TopicSequel.limit(1000).order(:id).select(:id, :title).each do |t|
160
+ s << t.id.to_s
161
+ s << t.title
162
+ end
163
+ s
164
+ end
165
+
166
+ def sequel_pluck_title_id
167
+ s = +""
168
+ TopicSequel.limit(1000).order(:id).select_map([:id, :title]).each do |t|
169
+ s << t[0].to_s
170
+ s << t[1]
171
+ end
172
+ s
173
+ end
174
+
175
+ # usage is not really recommended but just to compare to pluck lets have it
176
+ def mini_sql_title_id_query_single
177
+ s = +""
178
+ i = 0
179
+ r = $mini_sql.query_single(-"select id, title from topics order by id limit 1000")
180
+ while i < r.length
181
+ s << r[i].to_s
182
+ s << r[i+1]
183
+ i += 2
184
+ end
185
+ s
186
+ end
187
+
188
+ results = [
189
+ ar_title_id,
190
+ ar_title_id_pluck,
191
+ mysql_title_id,
192
+ mini_sql_title_id,
193
+ sequel_pluck_title_id,
194
+ sequel_select_title_id,
195
+ mini_sql_title_id_query_single
196
+ ]
197
+
198
+ exit(-1) unless results.uniq.length == 1
199
+
200
+
201
+ Benchmark.ips do |r|
202
+ r.report("ar select title id") do |n|
203
+ while n > 0
204
+ ar_title_id
205
+ n -= 1
206
+ end
207
+ end
208
+ r.report("ar select title id pluck") do |n|
209
+ while n > 0
210
+ ar_title_id_pluck
211
+ n -= 1
212
+ end
213
+ end
214
+ r.report("sequel title id select") do |n|
215
+ while n > 0
216
+ sequel_select_title_id
217
+ n -= 1
218
+ end
219
+ end
220
+ r.report("mysql select title id") do |n|
221
+ while n > 0
222
+ mysql_title_id
223
+ n -= 1
224
+ end
225
+ end
226
+ r.report("mini_sql select title id") do |n|
227
+ while n > 0
228
+ mini_sql_title_id
229
+ n -= 1
230
+ end
231
+ end
232
+ r.report("sequel title id pluck") do |n|
233
+ while n > 0
234
+ sequel_pluck_title_id
235
+ n -= 1
236
+ end
237
+ end
238
+ r.report("mini_sql query_single title id") do |n|
239
+ while n > 0
240
+ mini_sql_title_id_query_single
241
+ n -= 1
242
+ end
243
+ end
244
+ r.compare!
245
+ end
246
+
247
+
248
+
249
+ def wide_topic_ar
250
+ Topic.first
251
+ end
252
+
253
+ def wide_topic_mysql
254
+ r = $conn.query("select * from topics limit 1", as: :hash)
255
+ row = r.first
256
+ row
257
+ end
258
+
259
+ def wide_topic_sequel
260
+ TopicSequel.first
261
+ end
262
+
263
+ def wide_topic_mini_sql
264
+ $conn.query("select * from topics limit 1").first
265
+ end
266
+
267
+ Benchmark.ips do |r|
268
+ r.report("wide topic ar") do |n|
269
+ while n > 0
270
+ wide_topic_ar
271
+ n -= 1
272
+ end
273
+ end
274
+ r.report("wide topic sequel") do |n|
275
+ while n > 0
276
+ wide_topic_sequel
277
+ n -= 1
278
+ end
279
+ end
280
+ r.report("wide topic mysql") do |n|
281
+ while n > 0
282
+ wide_topic_mysql
283
+ n -= 1
284
+ end
285
+ end
286
+ r.report("wide topic mini sql") do |n|
287
+ while n > 0
288
+ wide_topic_mini_sql
289
+ n -= 1
290
+ end
291
+ end
292
+ r.compare!
293
+ end
294
+
295
+ # Comparison:
296
+ # mysql select title id: 485.0 i/s
297
+ # mini_sql query_single title id: 447.2 i/s - same-ish: difference falls within error
298
+ # mini_sql select title id: 417.4 i/s - 1.16x slower
299
+ # sequel title id pluck: 370.2 i/s - 1.31x slower
300
+ # sequel title id select: 351.0 i/s - 1.38x slower
301
+ # ar select title id pluck: 317.1 i/s - 1.53x slower
302
+ # ar select title id: 102.3 i/s - 4.74x slower
303
+
304
+
305
+ # Comparison:
306
+ # wide topic mini sql: 6768.7 i/s
307
+ # wide topic mysql: 6063.9 i/s - same-ish: difference falls within error
308
+ # wide topic sequel: 4908.6 i/s - same-ish: difference falls within error
309
+ # wide topic ar: 2630.2 i/s - 2.57x slower
310
+
@@ -148,7 +148,7 @@ def pg_title_id
148
148
  s
149
149
  end
150
150
 
151
- $mini_sql = MiniSql::Connection.new($conn)
151
+ $mini_sql = MiniSql::Connection.get($conn)
152
152
 
153
153
  def mini_sql_title_id
154
154
  s = +""
@@ -26,5 +26,10 @@ module MiniSql
26
26
  autoload :Connection, "mini_sql/sqlite/connection"
27
27
  autoload :DeserializerCache, "mini_sql/sqlite/deserializer_cache"
28
28
  end
29
+
30
+ module Mysql
31
+ autoload :Connection, "mini_sql/mysql/connection"
32
+ autoload :DeserializerCache, "mini_sql/mysql/deserializer_cache"
33
+ end
29
34
  end
30
35
  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
@@ -10,6 +10,8 @@ module MiniSql
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
@@ -0,0 +1,60 @@
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 exec(sql, *params)
24
+ run(sql, :array, params)
25
+ raw_connection.affected_rows
26
+ end
27
+
28
+ def query(sql, *params)
29
+ result = run(sql, :array, params)
30
+ @deserializer_cache.materialize(result)
31
+ end
32
+
33
+ def escape_string(str)
34
+ raw_connection.escape(str)
35
+ end
36
+
37
+ def build(sql)
38
+ Builder.new(self, sql)
39
+ end
40
+
41
+ private
42
+
43
+ def run(sql, as, params)
44
+ if params && params.length > 0
45
+ sql = param_encoder.encode(sql, *params)
46
+ end
47
+ raw_connection.query(
48
+ sql,
49
+ as: as,
50
+ database_timezone: :utc,
51
+ application_timezone: :utc,
52
+ cast_booleans: true,
53
+ cast: true,
54
+ cache_rows: true,
55
+ symbolize_keys: false
56
+ )
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,59 @@
1
+ module MiniSql
2
+ module Mysql
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
+ key = result.fields
14
+
15
+ # trivial fast LRU implementation
16
+ materializer = @cache.delete(key)
17
+ if materializer
18
+ @cache[key] = materializer
19
+ else
20
+ materializer = @cache[key] = new_row_matrializer(result)
21
+ @cache.shift if @cache.length > @max_size
22
+ end
23
+
24
+ result.map do |data|
25
+ materializer.materialize(data)
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def new_row_matrializer(result)
32
+ fields = result.fields
33
+
34
+ Class.new do
35
+ attr_accessor(*fields)
36
+
37
+ # AM serializer support
38
+ alias :read_attribute_for_serialization :send
39
+
40
+ def to_h
41
+ r = {}
42
+ instance_variables.each do |f|
43
+ r[f.to_s.sub('@','').to_sym] = instance_variable_get(f)
44
+ end
45
+ r
46
+ end
47
+
48
+ instance_eval <<~RUBY
49
+ def materialize(data)
50
+ r = self.new
51
+ #{col=-1; fields.map{|f| "r.#{f} = data[#{col+=1}]"}.join("; ")}
52
+ r
53
+ end
54
+ RUBY
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module MiniSql
2
- VERSION = "0.2.2"
3
+ VERSION = "0.2.3"
3
4
  end
@@ -35,6 +35,7 @@ Gem::Specification.new do |spec|
35
35
  spec.add_development_dependency "activerecord-jdbcpostgresql-adapter", "~> 52.2"
36
36
  else
37
37
  spec.add_development_dependency "pg", "> 1"
38
+ spec.add_development_dependency "mysql2"
38
39
  spec.add_development_dependency "sqlite3", "~> 1.3"
39
40
  end
40
41
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mini_sql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.3
5
5
  platform: java
6
6
  authors:
7
7
  - Sam Saffron
@@ -125,6 +125,7 @@ files:
125
125
  - README.md
126
126
  - Rakefile
127
127
  - bench/timestamp_perf.rb
128
+ - bench/topic_mysql_perf.rb
128
129
  - bench/topic_perf.rb
129
130
  - bin/console
130
131
  - bin/setup
@@ -133,6 +134,8 @@ files:
133
134
  - lib/mini_sql/connection.rb
134
135
  - lib/mini_sql/deserializer_cache.rb
135
136
  - lib/mini_sql/inline_param_encoder.rb
137
+ - lib/mini_sql/mysql/connection.rb
138
+ - lib/mini_sql/mysql/deserializer_cache.rb
136
139
  - lib/mini_sql/postgres/coders.rb
137
140
  - lib/mini_sql/postgres/connection.rb
138
141
  - lib/mini_sql/postgres/deserializer_cache.rb