mini_sql 0.2.4 → 1.1.0
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-https---raw-githubusercontent-com-discourse-discourse-master--rubocop-yml +355 -0
- data/.rubocop.yml +8 -0
- data/CHANGELOG +22 -0
- data/Gemfile +3 -1
- data/Guardfile +2 -0
- data/README.md +125 -1
- data/Rakefile +3 -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/timestamp_perf.rb +22 -21
- data/bench/topic_mysql_perf.rb +1 -7
- data/bench/topic_perf.rb +27 -169
- data/bench/topic_wide_perf.rb +92 -0
- data/bin/console +1 -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 +64 -24
- data/lib/mini_sql/connection.rb +15 -3
- data/lib/mini_sql/decoratable.rb +22 -0
- data/lib/mini_sql/deserializer_cache.rb +2 -0
- data/lib/mini_sql/inline_param_encoder.rb +12 -13
- data/lib/mini_sql/mysql/connection.rb +18 -3
- data/lib/mini_sql/mysql/deserializer_cache.rb +14 -16
- 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/coders.rb +2 -0
- data/lib/mini_sql/postgres/connection.rb +89 -0
- data/lib/mini_sql/postgres/deserializer_cache.rb +36 -16
- 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 +8 -1
- data/lib/mini_sql/postgres_jdbc/deserializer_cache.rb +43 -43
- data/lib/mini_sql/result.rb +30 -0
- data/lib/mini_sql/serializer.rb +84 -0
- data/lib/mini_sql/sqlite/connection.rb +20 -2
- data/lib/mini_sql/sqlite/deserializer_cache.rb +14 -16
- 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 +7 -2
- metadata +75 -11
- data/.travis.yml +0 -26
@@ -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)
|
@@ -26,6 +32,10 @@ module MiniSql
|
|
26
32
|
r
|
27
33
|
end
|
28
34
|
|
35
|
+
def query_array(sql, *params)
|
36
|
+
run(sql, *params)
|
37
|
+
end
|
38
|
+
|
29
39
|
def exec(sql, *params)
|
30
40
|
|
31
41
|
start = raw_connection.total_changes
|
@@ -46,8 +56,14 @@ module MiniSql
|
|
46
56
|
end
|
47
57
|
end
|
48
58
|
|
59
|
+
def query_decorator(decorator, sql, *params)
|
60
|
+
run(sql, *params) do |set|
|
61
|
+
deserializer_cache.materialize(set, decorator)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
49
65
|
def escape_string(str)
|
50
|
-
str.gsub("'","''")
|
66
|
+
str.gsub("'", "''")
|
51
67
|
end
|
52
68
|
|
53
69
|
private
|
@@ -58,7 +74,9 @@ module MiniSql
|
|
58
74
|
end
|
59
75
|
if block_given?
|
60
76
|
stmt = SQLite3::Statement.new(raw_connection, sql)
|
61
|
-
yield stmt.execute
|
77
|
+
result = yield stmt.execute
|
78
|
+
stmt.close
|
79
|
+
result
|
62
80
|
else
|
63
81
|
raw_connection.execute(sql)
|
64
82
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module MiniSql
|
2
4
|
module Sqlite
|
3
5
|
class DeserializerCache
|
@@ -9,19 +11,23 @@ module MiniSql
|
|
9
11
|
@max_size = max_size || DEFAULT_MAX_SIZE
|
10
12
|
end
|
11
13
|
|
12
|
-
def materialize(result)
|
14
|
+
def materialize(result, decorator_module = nil)
|
13
15
|
|
14
|
-
key = result.columns
|
16
|
+
key = result.columns.join(',')
|
15
17
|
|
16
18
|
# trivial fast LRU implementation
|
17
19
|
materializer = @cache.delete(key)
|
18
20
|
if materializer
|
19
21
|
@cache[key] = materializer
|
20
22
|
else
|
21
|
-
materializer = @cache[key] =
|
23
|
+
materializer = @cache[key] = new_row_materializer(result)
|
22
24
|
@cache.shift if @cache.length > @max_size
|
23
25
|
end
|
24
26
|
|
27
|
+
if decorator_module
|
28
|
+
materializer = materializer.decorated(decorator_module)
|
29
|
+
end
|
30
|
+
|
25
31
|
r = []
|
26
32
|
# quicker loop
|
27
33
|
while !result.eof?
|
@@ -35,27 +41,19 @@ module MiniSql
|
|
35
41
|
|
36
42
|
private
|
37
43
|
|
38
|
-
def
|
44
|
+
def new_row_materializer(result)
|
39
45
|
fields = result.columns
|
40
46
|
|
41
47
|
Class.new do
|
42
|
-
|
48
|
+
extend MiniSql::Decoratable
|
49
|
+
include MiniSql::Result
|
43
50
|
|
44
|
-
|
45
|
-
alias :read_attribute_for_serialization :send
|
46
|
-
|
47
|
-
def to_h
|
48
|
-
r = {}
|
49
|
-
instance_variables.each do |f|
|
50
|
-
r[f.to_s.sub('@','').to_sym] = instance_variable_get(f)
|
51
|
-
end
|
52
|
-
r
|
53
|
-
end
|
51
|
+
attr_accessor(*fields)
|
54
52
|
|
55
53
|
instance_eval <<~RUBY
|
56
54
|
def materialize(data)
|
57
55
|
r = self.new
|
58
|
-
#{col
|
56
|
+
#{col = -1; fields.map { |f| "r.#{f} = data[#{col += 1}]" }.join("; ")}
|
59
57
|
r
|
60
58
|
end
|
61
59
|
RUBY
|
@@ -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,17 +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
|
-
|
28
|
+
# rubocop:disable Discourse/NoChdir
|
29
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
29
30
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
30
31
|
end
|
32
|
+
# rubocop:enable Discourse/NoChdir
|
31
33
|
spec.require_paths = ["lib"]
|
32
34
|
|
33
35
|
spec.add_development_dependency "bundler", "> 1.16"
|
34
|
-
spec.add_development_dependency "rake", "
|
36
|
+
spec.add_development_dependency "rake", "> 10"
|
35
37
|
spec.add_development_dependency "minitest", "~> 5.0"
|
36
38
|
spec.add_development_dependency "guard", "~> 2.14"
|
37
39
|
spec.add_development_dependency "guard-minitest", "~> 2.4"
|
38
40
|
spec.add_development_dependency "activesupport", "~> 5.2"
|
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'
|
39
44
|
|
40
45
|
if RUBY_ENGINE == 'jruby'
|
41
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.0
|
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
|
@@ -94,6 +94,48 @@ dependencies:
|
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '5.2'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubocop
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 1.4.0
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 1.4.0
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rubocop-discourse
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::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
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: 1.5.1
|
97
139
|
- !ruby/object:Gem::Dependency
|
98
140
|
name: pg
|
99
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -143,8 +185,10 @@ executables: []
|
|
143
185
|
extensions: []
|
144
186
|
extra_rdoc_files: []
|
145
187
|
files:
|
188
|
+
- ".github/workflows/ci.yml"
|
146
189
|
- ".gitignore"
|
147
|
-
- ".
|
190
|
+
- ".rubocop-https---raw-githubusercontent-com-discourse-discourse-master--rubocop-yml"
|
191
|
+
- ".rubocop.yml"
|
148
192
|
- CHANGELOG
|
149
193
|
- CODE_OF_CONDUCT.md
|
150
194
|
- Gemfile
|
@@ -152,25 +196,45 @@ files:
|
|
152
196
|
- LICENSE.txt
|
153
197
|
- README.md
|
154
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
|
155
204
|
- bench/timestamp_perf.rb
|
156
205
|
- bench/topic_mysql_perf.rb
|
157
206
|
- bench/topic_perf.rb
|
207
|
+
- bench/topic_wide_perf.rb
|
158
208
|
- bin/console
|
159
209
|
- bin/setup
|
160
210
|
- lib/mini_sql.rb
|
211
|
+
- lib/mini_sql/abstract/prepared_binds.rb
|
212
|
+
- lib/mini_sql/abstract/prepared_cache.rb
|
161
213
|
- lib/mini_sql/builder.rb
|
162
214
|
- lib/mini_sql/connection.rb
|
215
|
+
- lib/mini_sql/decoratable.rb
|
163
216
|
- lib/mini_sql/deserializer_cache.rb
|
164
217
|
- lib/mini_sql/inline_param_encoder.rb
|
165
218
|
- lib/mini_sql/mysql/connection.rb
|
166
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
|
167
223
|
- lib/mini_sql/postgres/coders.rb
|
168
224
|
- lib/mini_sql/postgres/connection.rb
|
169
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
|
170
229
|
- lib/mini_sql/postgres_jdbc/connection.rb
|
171
230
|
- lib/mini_sql/postgres_jdbc/deserializer_cache.rb
|
231
|
+
- lib/mini_sql/result.rb
|
232
|
+
- lib/mini_sql/serializer.rb
|
172
233
|
- lib/mini_sql/sqlite/connection.rb
|
173
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
|
174
238
|
- lib/mini_sql/version.rb
|
175
239
|
- mini_sql.gemspec
|
176
240
|
homepage: https://github.com/discourse/mini_sql
|
@@ -180,7 +244,7 @@ metadata:
|
|
180
244
|
bug_tracker_uri: https://github.com/discourse/mini_sql/issues
|
181
245
|
source_code_uri: https://github.com/discourse/mini_sql
|
182
246
|
changelog_uri: https://github.com/discourse/mini_sql/blob/master/CHANGELOG
|
183
|
-
post_install_message:
|
247
|
+
post_install_message:
|
184
248
|
rdoc_options: []
|
185
249
|
require_paths:
|
186
250
|
- lib
|
@@ -195,8 +259,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
195
259
|
- !ruby/object:Gem::Version
|
196
260
|
version: '0'
|
197
261
|
requirements: []
|
198
|
-
rubygems_version: 3.
|
199
|
-
signing_key:
|
262
|
+
rubygems_version: 3.2.2
|
263
|
+
signing_key:
|
200
264
|
specification_version: 4
|
201
265
|
summary: A fast, safe, simple direct SQL executor
|
202
266
|
test_files: []
|