mini_sql 0.2.5 → 1.1.1
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 +4 -4
- data/.github/workflows/ci.yml +66 -0
- data/.rubocop.yml +5 -2
- data/CHANGELOG +22 -0
- data/README.md +66 -1
- data/bench/builder_perf.rb +138 -0
- data/bench/decorator_perf.rb +143 -0
- data/bench/mini_sql_methods_perf.rb +80 -0
- data/bench/prepared_perf.rb +59 -0
- data/bench/shared/generate_data.rb +133 -0
- data/bench/topic_perf.rb +21 -327
- data/bench/topic_wide_perf.rb +92 -0
- data/lib/mini_sql.rb +20 -8
- data/lib/mini_sql/abstract/prepared_binds.rb +74 -0
- data/lib/mini_sql/abstract/prepared_cache.rb +45 -0
- data/lib/mini_sql/builder.rb +66 -23
- data/lib/mini_sql/connection.rb +14 -2
- data/lib/mini_sql/decoratable.rb +22 -0
- data/lib/mini_sql/inline_param_encoder.rb +4 -5
- data/lib/mini_sql/mysql/connection.rb +6 -0
- data/lib/mini_sql/mysql/deserializer_cache.rb +9 -15
- data/lib/mini_sql/mysql/prepared_binds.rb +15 -0
- data/lib/mini_sql/mysql/prepared_cache.rb +21 -0
- data/lib/mini_sql/mysql/prepared_connection.rb +44 -0
- data/lib/mini_sql/postgres/connection.rb +75 -3
- data/lib/mini_sql/postgres/deserializer_cache.rb +32 -15
- data/lib/mini_sql/postgres/prepared_binds.rb +15 -0
- data/lib/mini_sql/postgres/prepared_cache.rb +25 -0
- data/lib/mini_sql/postgres/prepared_connection.rb +36 -0
- data/lib/mini_sql/postgres_jdbc/connection.rb +3 -1
- data/lib/mini_sql/postgres_jdbc/deserializer_cache.rb +10 -14
- data/lib/mini_sql/result.rb +30 -0
- data/lib/mini_sql/serializer.rb +84 -0
- data/lib/mini_sql/sqlite/connection.rb +9 -1
- data/lib/mini_sql/sqlite/deserializer_cache.rb +9 -15
- data/lib/mini_sql/sqlite/prepared_binds.rb +15 -0
- data/lib/mini_sql/sqlite/prepared_cache.rb +21 -0
- data/lib/mini_sql/sqlite/prepared_connection.rb +40 -0
- data/lib/mini_sql/version.rb +1 -1
- data/mini_sql.gemspec +6 -5
- metadata +49 -15
- data/.travis.yml +0 -28
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MiniSql
|
4
|
+
module Result
|
5
|
+
# AM serializer support
|
6
|
+
alias :read_attribute_for_serialization :send
|
7
|
+
|
8
|
+
def to_h
|
9
|
+
r = {}
|
10
|
+
instance_variables.each do |f|
|
11
|
+
r[f.to_s.delete_prefix('@').to_sym] = instance_variable_get(f)
|
12
|
+
end
|
13
|
+
r
|
14
|
+
end
|
15
|
+
|
16
|
+
def values
|
17
|
+
instance_variables.map { |f| instance_variable_get(f) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def ==(other_result)
|
21
|
+
self.class.decorator == other_result.class.decorator &&
|
22
|
+
self.instance_variables == other_result.instance_variables &&
|
23
|
+
self.values == other_result.values
|
24
|
+
end
|
25
|
+
|
26
|
+
def eql?(other_result)
|
27
|
+
self == other_result
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MiniSql
|
4
|
+
class Serializer < Array
|
5
|
+
MAX_CACHE_SIZE = 500
|
6
|
+
|
7
|
+
def initialize(result)
|
8
|
+
replace(result)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.marshallable(result)
|
12
|
+
new(result)
|
13
|
+
end
|
14
|
+
|
15
|
+
def marshal_dump
|
16
|
+
serialize
|
17
|
+
end
|
18
|
+
|
19
|
+
def marshal_load(wrapper)
|
20
|
+
replace self.class.materialize(wrapper)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def serialize
|
26
|
+
if length == 0
|
27
|
+
{}
|
28
|
+
else
|
29
|
+
{
|
30
|
+
"decorator" => first.class.decorator.to_s,
|
31
|
+
"fields" => first.to_h.keys,
|
32
|
+
"data" => map(&:values),
|
33
|
+
}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.materialize(wrapper)
|
38
|
+
if !wrapper["data"]
|
39
|
+
[]
|
40
|
+
else
|
41
|
+
materializer = cached_materializer(wrapper['fields'], wrapper['decorator'])
|
42
|
+
wrapper["data"].map do |row|
|
43
|
+
materializer.materialize(row)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.cached_materializer(fields, decorator_module = nil)
|
49
|
+
@cache ||= {}
|
50
|
+
key = fields
|
51
|
+
m = @cache.delete(key)
|
52
|
+
if m
|
53
|
+
@cache[key] = m
|
54
|
+
else
|
55
|
+
m = @cache[key] = materializer(fields)
|
56
|
+
@cache.shift if @cache.length > MAX_CACHE_SIZE
|
57
|
+
end
|
58
|
+
|
59
|
+
if decorator_module && decorator_module.length > 0
|
60
|
+
decorator = Kernel.const_get(decorator_module)
|
61
|
+
m = m.decorated(decorator)
|
62
|
+
end
|
63
|
+
|
64
|
+
m
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.materializer(fields)
|
68
|
+
Class.new do
|
69
|
+
extend MiniSql::Decoratable
|
70
|
+
include MiniSql::Result
|
71
|
+
|
72
|
+
attr_accessor(*fields)
|
73
|
+
|
74
|
+
instance_eval <<~RUBY
|
75
|
+
def materialize(values)
|
76
|
+
r = self.new
|
77
|
+
#{col = -1; fields.map { |f| "r.#{f} = values[#{col += 1}]" }.join("; ")}
|
78
|
+
r
|
79
|
+
end
|
80
|
+
RUBY
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -9,6 +9,12 @@ module MiniSql
|
|
9
9
|
@raw_connection = raw_connection
|
10
10
|
@param_encoder = (args && args[:param_encoder]) || InlineParamEncoder.new(self)
|
11
11
|
@deserializer_cache = (args && args[:deserializer_cache]) || DeserializerCache.new
|
12
|
+
|
13
|
+
@prepared = PreparedConnection.new(self, @deserializer_cache)
|
14
|
+
end
|
15
|
+
|
16
|
+
def prepared(condition = true)
|
17
|
+
condition ? @prepared : self
|
12
18
|
end
|
13
19
|
|
14
20
|
def query_single(sql, *params)
|
@@ -68,7 +74,9 @@ module MiniSql
|
|
68
74
|
end
|
69
75
|
if block_given?
|
70
76
|
stmt = SQLite3::Statement.new(raw_connection, sql)
|
71
|
-
yield stmt.execute
|
77
|
+
result = yield stmt.execute
|
78
|
+
stmt.close
|
79
|
+
result
|
72
80
|
else
|
73
81
|
raw_connection.execute(sql)
|
74
82
|
end
|
@@ -13,18 +13,20 @@ module MiniSql
|
|
13
13
|
|
14
14
|
def materialize(result, decorator_module = nil)
|
15
15
|
|
16
|
-
key = result.columns
|
16
|
+
key = result.columns.join(',')
|
17
17
|
|
18
18
|
# trivial fast LRU implementation
|
19
19
|
materializer = @cache.delete(key)
|
20
20
|
if materializer
|
21
21
|
@cache[key] = materializer
|
22
22
|
else
|
23
|
-
materializer = @cache[key] =
|
23
|
+
materializer = @cache[key] = new_row_materializer(result)
|
24
24
|
@cache.shift if @cache.length > @max_size
|
25
25
|
end
|
26
26
|
|
27
|
-
|
27
|
+
if decorator_module
|
28
|
+
materializer = materializer.decorated(decorator_module)
|
29
|
+
end
|
28
30
|
|
29
31
|
r = []
|
30
32
|
# quicker loop
|
@@ -39,22 +41,14 @@ module MiniSql
|
|
39
41
|
|
40
42
|
private
|
41
43
|
|
42
|
-
def
|
44
|
+
def new_row_materializer(result)
|
43
45
|
fields = result.columns
|
44
46
|
|
45
47
|
Class.new do
|
46
|
-
|
47
|
-
|
48
|
-
# AM serializer support
|
49
|
-
alias :read_attribute_for_serialization :send
|
48
|
+
extend MiniSql::Decoratable
|
49
|
+
include MiniSql::Result
|
50
50
|
|
51
|
-
|
52
|
-
r = {}
|
53
|
-
instance_variables.each do |f|
|
54
|
-
r[f.to_s.sub('@', '').to_sym] = instance_variable_get(f)
|
55
|
-
end
|
56
|
-
r
|
57
|
-
end
|
51
|
+
attr_accessor(*fields)
|
58
52
|
|
59
53
|
instance_eval <<~RUBY
|
60
54
|
def materialize(data)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "mini_sql/abstract/prepared_cache"
|
4
|
+
|
5
|
+
module MiniSql
|
6
|
+
module Sqlite
|
7
|
+
class PreparedCache < MiniSql::Abstract::PreparedCache
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def alloc(sql)
|
12
|
+
@connection.prepare(sql)
|
13
|
+
end
|
14
|
+
|
15
|
+
def dealloc(statement)
|
16
|
+
statement.close unless statement.closed?
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MiniSql
|
4
|
+
module Sqlite
|
5
|
+
class PreparedConnection < Connection
|
6
|
+
|
7
|
+
attr_reader :unprepared
|
8
|
+
|
9
|
+
def initialize(unprepared_connection, deserializer_cache)
|
10
|
+
@unprepared = unprepared_connection
|
11
|
+
@raw_connection = unprepared_connection.raw_connection
|
12
|
+
@deserializer_cache = deserializer_cache
|
13
|
+
@param_encoder = unprepared_connection.param_encoder
|
14
|
+
|
15
|
+
@prepared_cache = PreparedCache.new(@raw_connection)
|
16
|
+
@param_binder = PreparedBinds.new
|
17
|
+
end
|
18
|
+
|
19
|
+
def build(_)
|
20
|
+
raise 'Builder can not be called on prepared connections, instead of `::MINI_SQL.prepared.build(sql).query` use `::MINI_SQL.build(sql).prepared.query`'
|
21
|
+
end
|
22
|
+
|
23
|
+
def prepared(condition = true)
|
24
|
+
condition ? self : @unprepared
|
25
|
+
end
|
26
|
+
|
27
|
+
private def run(sql, params)
|
28
|
+
prepared_sql, binds, _bind_names = @param_binder.bind(sql, params)
|
29
|
+
statement = @prepared_cache.prepare_statement(prepared_sql)
|
30
|
+
statement.bind_params(binds)
|
31
|
+
if block_given?
|
32
|
+
yield statement.execute
|
33
|
+
else
|
34
|
+
statement.execute.to_a
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/mini_sql/version.rb
CHANGED
data/mini_sql.gemspec
CHANGED
@@ -25,21 +25,22 @@ Gem::Specification.new do |spec|
|
|
25
25
|
|
26
26
|
# Specify which files should be added to the gem when it is released.
|
27
27
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
28
|
-
# rubocop:disable
|
28
|
+
# rubocop:disable Discourse/NoChdir
|
29
29
|
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
30
30
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
31
31
|
end
|
32
|
-
# rubocop:enable
|
32
|
+
# rubocop:enable Discourse/NoChdir
|
33
33
|
spec.require_paths = ["lib"]
|
34
34
|
|
35
35
|
spec.add_development_dependency "bundler", "> 1.16"
|
36
|
-
spec.add_development_dependency "rake", "
|
36
|
+
spec.add_development_dependency "rake", "> 10"
|
37
37
|
spec.add_development_dependency "minitest", "~> 5.0"
|
38
38
|
spec.add_development_dependency "guard", "~> 2.14"
|
39
39
|
spec.add_development_dependency "guard-minitest", "~> 2.4"
|
40
40
|
spec.add_development_dependency "activesupport", "~> 5.2"
|
41
|
-
spec.add_development_dependency 'rubocop', '~>
|
42
|
-
spec.add_development_dependency 'rubocop-discourse', '~>
|
41
|
+
spec.add_development_dependency 'rubocop', '~> 1.4.0'
|
42
|
+
spec.add_development_dependency 'rubocop-discourse', '~> 2.4.1'
|
43
|
+
spec.add_development_dependency 'm', '~> 1.5.1'
|
43
44
|
|
44
45
|
if RUBY_ENGINE == 'jruby'
|
45
46
|
spec.add_development_dependency "activerecord-jdbcpostgresql-adapter", "~> 52.2"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mini_sql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Saffron
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-03-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -28,16 +28,16 @@ dependencies:
|
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '10
|
33
|
+
version: '10'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - ">"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '10
|
40
|
+
version: '10'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: minitest
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -100,28 +100,42 @@ dependencies:
|
|
100
100
|
requirements:
|
101
101
|
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version:
|
103
|
+
version: 1.4.0
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
108
|
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version:
|
110
|
+
version: 1.4.0
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
112
|
name: rubocop-discourse
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
115
|
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version:
|
117
|
+
version: 2.4.1
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 2.4.1
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: m
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 1.5.1
|
118
132
|
type: :development
|
119
133
|
prerelease: false
|
120
134
|
version_requirements: !ruby/object:Gem::Requirement
|
121
135
|
requirements:
|
122
136
|
- - "~>"
|
123
137
|
- !ruby/object:Gem::Version
|
124
|
-
version: 1.
|
138
|
+
version: 1.5.1
|
125
139
|
- !ruby/object:Gem::Dependency
|
126
140
|
name: pg
|
127
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -171,10 +185,10 @@ executables: []
|
|
171
185
|
extensions: []
|
172
186
|
extra_rdoc_files: []
|
173
187
|
files:
|
188
|
+
- ".github/workflows/ci.yml"
|
174
189
|
- ".gitignore"
|
175
190
|
- ".rubocop-https---raw-githubusercontent-com-discourse-discourse-master--rubocop-yml"
|
176
191
|
- ".rubocop.yml"
|
177
|
-
- ".travis.yml"
|
178
192
|
- CHANGELOG
|
179
193
|
- CODE_OF_CONDUCT.md
|
180
194
|
- Gemfile
|
@@ -182,25 +196,45 @@ files:
|
|
182
196
|
- LICENSE.txt
|
183
197
|
- README.md
|
184
198
|
- Rakefile
|
199
|
+
- bench/builder_perf.rb
|
200
|
+
- bench/decorator_perf.rb
|
201
|
+
- bench/mini_sql_methods_perf.rb
|
202
|
+
- bench/prepared_perf.rb
|
203
|
+
- bench/shared/generate_data.rb
|
185
204
|
- bench/timestamp_perf.rb
|
186
205
|
- bench/topic_mysql_perf.rb
|
187
206
|
- bench/topic_perf.rb
|
207
|
+
- bench/topic_wide_perf.rb
|
188
208
|
- bin/console
|
189
209
|
- bin/setup
|
190
210
|
- lib/mini_sql.rb
|
211
|
+
- lib/mini_sql/abstract/prepared_binds.rb
|
212
|
+
- lib/mini_sql/abstract/prepared_cache.rb
|
191
213
|
- lib/mini_sql/builder.rb
|
192
214
|
- lib/mini_sql/connection.rb
|
215
|
+
- lib/mini_sql/decoratable.rb
|
193
216
|
- lib/mini_sql/deserializer_cache.rb
|
194
217
|
- lib/mini_sql/inline_param_encoder.rb
|
195
218
|
- lib/mini_sql/mysql/connection.rb
|
196
219
|
- lib/mini_sql/mysql/deserializer_cache.rb
|
220
|
+
- lib/mini_sql/mysql/prepared_binds.rb
|
221
|
+
- lib/mini_sql/mysql/prepared_cache.rb
|
222
|
+
- lib/mini_sql/mysql/prepared_connection.rb
|
197
223
|
- lib/mini_sql/postgres/coders.rb
|
198
224
|
- lib/mini_sql/postgres/connection.rb
|
199
225
|
- lib/mini_sql/postgres/deserializer_cache.rb
|
226
|
+
- lib/mini_sql/postgres/prepared_binds.rb
|
227
|
+
- lib/mini_sql/postgres/prepared_cache.rb
|
228
|
+
- lib/mini_sql/postgres/prepared_connection.rb
|
200
229
|
- lib/mini_sql/postgres_jdbc/connection.rb
|
201
230
|
- lib/mini_sql/postgres_jdbc/deserializer_cache.rb
|
231
|
+
- lib/mini_sql/result.rb
|
232
|
+
- lib/mini_sql/serializer.rb
|
202
233
|
- lib/mini_sql/sqlite/connection.rb
|
203
234
|
- lib/mini_sql/sqlite/deserializer_cache.rb
|
235
|
+
- lib/mini_sql/sqlite/prepared_binds.rb
|
236
|
+
- lib/mini_sql/sqlite/prepared_cache.rb
|
237
|
+
- lib/mini_sql/sqlite/prepared_connection.rb
|
204
238
|
- lib/mini_sql/version.rb
|
205
239
|
- mini_sql.gemspec
|
206
240
|
homepage: https://github.com/discourse/mini_sql
|
@@ -210,7 +244,7 @@ metadata:
|
|
210
244
|
bug_tracker_uri: https://github.com/discourse/mini_sql/issues
|
211
245
|
source_code_uri: https://github.com/discourse/mini_sql
|
212
246
|
changelog_uri: https://github.com/discourse/mini_sql/blob/master/CHANGELOG
|
213
|
-
post_install_message:
|
247
|
+
post_install_message:
|
214
248
|
rdoc_options: []
|
215
249
|
require_paths:
|
216
250
|
- lib
|
@@ -225,8 +259,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
225
259
|
- !ruby/object:Gem::Version
|
226
260
|
version: '0'
|
227
261
|
requirements: []
|
228
|
-
rubygems_version: 3.
|
229
|
-
signing_key:
|
262
|
+
rubygems_version: 3.2.2
|
263
|
+
signing_key:
|
230
264
|
specification_version: 4
|
231
265
|
summary: A fast, safe, simple direct SQL executor
|
232
266
|
test_files: []
|