mini_sql 0.2.2-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.
@@ -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
+