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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +66 -0
  3. data/.rubocop.yml +5 -2
  4. data/CHANGELOG +22 -0
  5. data/README.md +66 -1
  6. data/bench/builder_perf.rb +138 -0
  7. data/bench/decorator_perf.rb +143 -0
  8. data/bench/mini_sql_methods_perf.rb +80 -0
  9. data/bench/prepared_perf.rb +59 -0
  10. data/bench/shared/generate_data.rb +133 -0
  11. data/bench/topic_perf.rb +21 -327
  12. data/bench/topic_wide_perf.rb +92 -0
  13. data/lib/mini_sql.rb +20 -8
  14. data/lib/mini_sql/abstract/prepared_binds.rb +74 -0
  15. data/lib/mini_sql/abstract/prepared_cache.rb +45 -0
  16. data/lib/mini_sql/builder.rb +66 -23
  17. data/lib/mini_sql/connection.rb +14 -2
  18. data/lib/mini_sql/decoratable.rb +22 -0
  19. data/lib/mini_sql/inline_param_encoder.rb +4 -5
  20. data/lib/mini_sql/mysql/connection.rb +6 -0
  21. data/lib/mini_sql/mysql/deserializer_cache.rb +9 -15
  22. data/lib/mini_sql/mysql/prepared_binds.rb +15 -0
  23. data/lib/mini_sql/mysql/prepared_cache.rb +21 -0
  24. data/lib/mini_sql/mysql/prepared_connection.rb +44 -0
  25. data/lib/mini_sql/postgres/connection.rb +75 -3
  26. data/lib/mini_sql/postgres/deserializer_cache.rb +32 -15
  27. data/lib/mini_sql/postgres/prepared_binds.rb +15 -0
  28. data/lib/mini_sql/postgres/prepared_cache.rb +25 -0
  29. data/lib/mini_sql/postgres/prepared_connection.rb +36 -0
  30. data/lib/mini_sql/postgres_jdbc/connection.rb +3 -1
  31. data/lib/mini_sql/postgres_jdbc/deserializer_cache.rb +10 -14
  32. data/lib/mini_sql/result.rb +30 -0
  33. data/lib/mini_sql/serializer.rb +84 -0
  34. data/lib/mini_sql/sqlite/connection.rb +9 -1
  35. data/lib/mini_sql/sqlite/deserializer_cache.rb +9 -15
  36. data/lib/mini_sql/sqlite/prepared_binds.rb +15 -0
  37. data/lib/mini_sql/sqlite/prepared_cache.rb +21 -0
  38. data/lib/mini_sql/sqlite/prepared_connection.rb +40 -0
  39. data/lib/mini_sql/version.rb +1 -1
  40. data/mini_sql.gemspec +6 -5
  41. metadata +49 -15
  42. 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] = new_row_matrializer(result)
23
+ materializer = @cache[key] = new_row_materializer(result)
24
24
  @cache.shift if @cache.length > @max_size
25
25
  end
26
26
 
27
- materializer.include(decorator_module) if decorator_module
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 new_row_matrializer(result)
44
+ def new_row_materializer(result)
43
45
  fields = result.columns
44
46
 
45
47
  Class.new do
46
- attr_accessor(*fields)
47
-
48
- # AM serializer support
49
- alias :read_attribute_for_serialization :send
48
+ extend MiniSql::Decoratable
49
+ include MiniSql::Result
50
50
 
51
- def to_h
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,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mini_sql/abstract/prepared_binds"
4
+
5
+ module MiniSql
6
+ module Sqlite
7
+ class PreparedBinds < ::MiniSql::Abstract::PreparedBinds
8
+
9
+ def bind_output(i)
10
+ '?'
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -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
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module MiniSql
3
- VERSION = "0.2.5"
3
+ VERSION = "1.1.1"
4
4
  end
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 DiscoruseCops/NoChdir
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 DiscoruseCops/NoChdir
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", "~> 10.0"
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', '~> 0.79.0'
42
- spec.add_development_dependency 'rubocop-discourse', '~> 1.0.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'
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: 0.2.5
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: 2020-04-07 00:00:00.000000000 Z
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.0'
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.0'
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: 0.79.0
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: 0.79.0
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: 1.0.2
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.0.2
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.0.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: []