mini_sql 0.2.2-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,347 @@
1
+ require 'bundler/inline'
2
+
3
+ gemfile do
4
+ source 'https://rubygems.org'
5
+ gem 'pg', github: 'ged/ruby-pg'
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
+ gem 'sequel_pg', github: 'jeremyevans/sequel_pg', require: 'sequel'
14
+ gem 'swift-db-postgres', github: 'deepfryed/swift-db-postgres'
15
+ end
16
+
17
+ require 'sequel'
18
+ require 'active_record'
19
+ require 'memory_profiler'
20
+ require 'benchmark/ips'
21
+ require 'mini_sql'
22
+
23
+ ActiveRecord::Base.establish_connection(
24
+ :adapter => "postgresql",
25
+ :database => "test_db"
26
+ )
27
+
28
+ DB = Sequel.postgres('test_db')
29
+
30
+ pg = ActiveRecord::Base.connection.raw_connection
31
+
32
+ pg.async_exec <<SQL
33
+ drop table if exists topics
34
+ SQL
35
+
36
+ pg.async_exec <<SQL
37
+ CREATE TABLE topics (
38
+ id integer NOT NULL PRIMARY KEY,
39
+ title character varying NOT NULL,
40
+ last_posted_at timestamp without time zone,
41
+ created_at timestamp without time zone NOT NULL,
42
+ updated_at timestamp without time zone NOT NULL,
43
+ views integer DEFAULT 0 NOT NULL,
44
+ posts_count integer DEFAULT 0 NOT NULL,
45
+ user_id integer,
46
+ last_post_user_id integer NOT NULL,
47
+ reply_count integer DEFAULT 0 NOT NULL,
48
+ featured_user1_id integer,
49
+ featured_user2_id integer,
50
+ featured_user3_id integer,
51
+ avg_time integer,
52
+ deleted_at timestamp without time zone,
53
+ highest_post_number integer DEFAULT 0 NOT NULL,
54
+ image_url character varying,
55
+ like_count integer DEFAULT 0 NOT NULL,
56
+ incoming_link_count integer DEFAULT 0 NOT NULL,
57
+ category_id integer,
58
+ visible boolean DEFAULT true NOT NULL,
59
+ moderator_posts_count integer DEFAULT 0 NOT NULL,
60
+ closed boolean DEFAULT false NOT NULL,
61
+ archived boolean DEFAULT false NOT NULL,
62
+ bumped_at timestamp without time zone NOT NULL,
63
+ has_summary boolean DEFAULT false NOT NULL,
64
+ vote_count integer DEFAULT 0 NOT NULL,
65
+ archetype character varying DEFAULT 'regular'::character varying NOT NULL,
66
+ featured_user4_id integer,
67
+ notify_moderators_count integer DEFAULT 0 NOT NULL,
68
+ spam_count integer DEFAULT 0 NOT NULL,
69
+ pinned_at timestamp without time zone,
70
+ score double precision,
71
+ percent_rank double precision DEFAULT 1.0 NOT NULL,
72
+ subtype character varying,
73
+ slug character varying,
74
+ deleted_by_id integer,
75
+ participant_count integer DEFAULT 1,
76
+ word_count integer,
77
+ excerpt character varying(1000),
78
+ pinned_globally boolean DEFAULT false NOT NULL,
79
+ pinned_until timestamp without time zone,
80
+ fancy_title character varying(400),
81
+ highest_staff_post_number integer DEFAULT 0 NOT NULL,
82
+ featured_link character varying
83
+ )
84
+ SQL
85
+
86
+ class Topic < ActiveRecord::Base
87
+ end
88
+
89
+ class TopicSequel < Sequel::Model(:topics)
90
+ end
91
+
92
+
93
+ Topic.transaction do
94
+ topic = {
95
+ }
96
+ Topic.columns.each do |c|
97
+ topic[c.name.to_sym] = case c.type
98
+ when :integer then 1
99
+ when :datetime then Time.now
100
+ when :boolean then false
101
+ else "HELLO WORLD" * 2
102
+ end
103
+ end
104
+
105
+ 1000.times do |id|
106
+ topic[:id] = id
107
+ Topic.create!(topic)
108
+ end
109
+ end
110
+
111
+ $conn = ActiveRecord::Base.connection.raw_connection
112
+
113
+ def ar_title_id_pluck
114
+ s = +""
115
+ Topic.limit(1000).order(:id).pluck(:id, :title).each do |id, title|
116
+ s << id.to_s
117
+ s << title
118
+ end
119
+ s
120
+ end
121
+
122
+ def ar_title_id
123
+ s = +""
124
+ Topic.limit(1000).order(:id).select(:id, :title).each do |t|
125
+ s << t.id.to_s
126
+ s << t.title
127
+ end
128
+ s
129
+ end
130
+
131
+ def pg_title_id
132
+ s = +""
133
+ # use the safe pattern here
134
+ r = $conn.async_exec(-"select id, title from topics order by id limit 1000")
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
146
+ end
147
+ r.clear
148
+ s
149
+ end
150
+
151
+ $mini_sql = MiniSql::Connection.new($conn)
152
+
153
+ def mini_sql_title_id
154
+ s = +""
155
+ $mini_sql.query(-"select id, title from topics order by id limit 1000").each do |t|
156
+ s << t.id.to_s
157
+ s << t.title
158
+ end
159
+ s
160
+ end
161
+
162
+ def sequel_select_title_id
163
+ s = +""
164
+ TopicSequel.limit(1000).order(:id).select(:id, :title).each do |t|
165
+ s << t.id.to_s
166
+ s << t.title
167
+ end
168
+ s
169
+ end
170
+
171
+ def sequel_pluck_title_id
172
+ s = +""
173
+ TopicSequel.limit(1000).order(:id).select_map([:id, :title]).each do |t|
174
+ s << t[0].to_s
175
+ s << t[1]
176
+ end
177
+ s
178
+ end
179
+
180
+ # usage is not really recommended but just to compare to pluck lets have it
181
+ def mini_sql_title_id_query_single
182
+ s = +""
183
+ i = 0
184
+ r = $mini_sql.query_single(-"select id, title from topics order by id limit 1000")
185
+ while i < r.length
186
+ s << r[i].to_s
187
+ s << r[i+1]
188
+ i += 2
189
+ end
190
+ s
191
+ end
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
+
208
+ results = [
209
+ ar_title_id,
210
+ ar_title_id_pluck,
211
+ pg_title_id,
212
+ mini_sql_title_id,
213
+ sequel_pluck_title_id,
214
+ sequel_select_title_id,
215
+ mini_sql_title_id_query_single,
216
+ swift_select_title_id
217
+ ]
218
+
219
+ exit(-1) unless results.uniq.length == 1
220
+
221
+
222
+ Benchmark.ips do |r|
223
+ r.report("ar select title id") do |n|
224
+ while n > 0
225
+ ar_title_id
226
+ n -= 1
227
+ end
228
+ end
229
+ r.report("ar select title id pluck") do |n|
230
+ while n > 0
231
+ ar_title_id_pluck
232
+ n -= 1
233
+ end
234
+ end
235
+ r.report("sequel title id select") do |n|
236
+ while n > 0
237
+ sequel_select_title_id
238
+ n -= 1
239
+ end
240
+ end
241
+ r.report("pg select title id") do |n|
242
+ while n > 0
243
+ pg_title_id
244
+ n -= 1
245
+ end
246
+ end
247
+ r.report("mini_sql select title id") do |n|
248
+ while n > 0
249
+ mini_sql_title_id
250
+ n -= 1
251
+ end
252
+ end
253
+ r.report("sequel title id pluck") do |n|
254
+ while n > 0
255
+ sequel_pluck_title_id
256
+ n -= 1
257
+ end
258
+ end
259
+ r.report("mini_sql query_single title id") do |n|
260
+ while n > 0
261
+ mini_sql_title_id_query_single
262
+ n -= 1
263
+ end
264
+ end
265
+ r.report("swift title id") do |n|
266
+ while n > 0
267
+ swift_select_title_id
268
+ n -= 1
269
+ end
270
+ end
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|
297
+ while n > 0
298
+ wide_topic_ar
299
+ n -= 1
300
+ end
301
+ end
302
+ r.report("wide topic sequel") do |n|
303
+ while n > 0
304
+ wide_topic_sequel
305
+ n -= 1
306
+ end
307
+ end
308
+ r.report("wide topic pg") do |n|
309
+ while n > 0
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
317
+ n -= 1
318
+ end
319
+ end
320
+ r.compare!
321
+ end
322
+
323
+
324
+ # Comparison:
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
333
+ #
334
+ #
335
+ # Comparison:
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
+
342
+
343
+ # to run deep analysis run
344
+ # MemoryProfiler.report do
345
+ # ar
346
+ # end.pretty_print
347
+
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "mini_sql"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ # we need this for a coder
4
+ require "bigdecimal"
5
+
6
+ require_relative "mini_sql/version"
7
+ require_relative "mini_sql/connection"
8
+ require_relative "mini_sql/deserializer_cache"
9
+ require_relative "mini_sql/builder"
10
+ require_relative "mini_sql/inline_param_encoder"
11
+
12
+ module MiniSql
13
+ if RUBY_ENGINE == 'jruby'
14
+ module Postgres
15
+ autoload :Connection, "mini_sql/postgres_jdbc/connection"
16
+ autoload :DeserializerCache, "mini_sql/postgres_jdbc/deserializer_cache"
17
+ end
18
+ else
19
+ module Postgres
20
+ autoload :Coders, "mini_sql/postgres/coders"
21
+ autoload :Connection, "mini_sql/postgres/connection"
22
+ autoload :DeserializerCache, "mini_sql/postgres/deserializer_cache"
23
+ end
24
+
25
+ module Sqlite
26
+ autoload :Connection, "mini_sql/sqlite/connection"
27
+ autoload :DeserializerCache, "mini_sql/sqlite/deserializer_cache"
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MiniSql::Builder
4
+
5
+ def initialize(connection, template)
6
+ @args = nil
7
+ @sql = template
8
+ @sections = {}
9
+ @connection = connection
10
+ end
11
+
12
+ [:set, :where2, :where, :order_by, :limit, :left_join, :join, :offset, :select].each do |k|
13
+ define_method k do |data, *args|
14
+ if args && (args.length == 1) && (Hash === args[0])
15
+ @args ||= {}
16
+ @args.merge!(args[0])
17
+ elsif args && args.length > 0
18
+ data = @connection.param_encoder.encode(data, *args)
19
+ end
20
+ @sections[k] ||= []
21
+ @sections[k] << data
22
+ self
23
+ end
24
+ end
25
+
26
+ def to_sql
27
+ sql = @sql.dup
28
+
29
+ @sections.each do |k, v|
30
+ joined = nil
31
+ case k
32
+ when :select
33
+ joined = (+"SELECT ") << v.join(" , ")
34
+ when :where, :where2
35
+ joined = (+"WHERE ") << v.map { |c| (+"(") << c << ")" }.join(" AND ")
36
+ when :join
37
+ joined = v.map { |item| (+"JOIN ") << item }.join("\n")
38
+ when :left_join
39
+ joined = v.map { |item| (+"LEFT JOIN ") << item }.join("\n")
40
+ when :limit
41
+ joined = (+"LIMIT ") << v.last.to_i.to_s
42
+ when :offset
43
+ joined = (+"OFFSET ") << v.last.to_i.to_s
44
+ when :order_by
45
+ joined = (+"ORDER BY ") << v.join(" , ")
46
+ when :set
47
+ joined = (+"SET ") << v.join(" , ")
48
+ end
49
+
50
+ sql.sub!("/*#{k}*/", joined)
51
+ end
52
+ sql
53
+ end
54
+
55
+ [:query, :query_single, :query_hash, :exec].each do |m|
56
+ class_eval <<~RUBY
57
+ def #{m}(hash_args = nil)
58
+ hash_args = @args.merge(hash_args) if hash_args && @args
59
+ hash_args ||= @args
60
+
61
+ if hash_args
62
+ @connection.#{m}(to_sql, hash_args)
63
+ else
64
+ @connection.#{m}(to_sql)
65
+ end
66
+ end
67
+ RUBY
68
+ end
69
+
70
+ end
71
+