mini_sql 0.3 → 1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3ea4564f53848c314c76c45db0c2af652c1e128ebfc1ba52d6ae11b3799149f4
4
- data.tar.gz: ecdfdc07fdf11e60338bc2ceb0415cbcc027720c5bba645281217a51befc2402
3
+ metadata.gz: 3d668e0b456e27a572040825399ca9477d6c4404bfc09e6e87b1330a106491a0
4
+ data.tar.gz: 3fb2027b34d3837e06f6c92b7075f8811296c0750c5c4c3ae48d6053189a4000
5
5
  SHA512:
6
- metadata.gz: 76a040187f4932dc9fbc80ae3323633a38462753caec47233dbfad3e276e90d8eeb0fbddcbfe974ade3b0b99c18a487d5ef865ebdb1effc67048352ecd35d99d
7
- data.tar.gz: 73c3519bf51a7934af89827830e3e7c45366eec01b1d65dc0c61afb2b87c12b093e9f3db99b1c4db081e6a6a1e983fd24d88db3aa13f63b9e546af0dce36beae
6
+ metadata.gz: 60449b0a92a13aa471dbc1dd6db2b8ba0571bca67f81bd46644bf8678ab794b5447e59cd3ea7a24d4732bcdb8eca44c9dfec748c57418d826444305395f62190
7
+ data.tar.gz: a249f769da221d659d1b4dceaf3dd153578fbda3f0951bdac938d178923a54ad852106629d76be38b5c4ad83c4175592b9971223f8e0aab49e91a48747ad7148
@@ -0,0 +1,66 @@
1
+ name: Mini SQL Tests
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches:
7
+ - master
8
+
9
+ env:
10
+ PGHOST: localhost
11
+ PGPORT: 5432
12
+ PGPASSWORD: postgres
13
+ PGUSER: postgres
14
+ MINI_SQL_MYSQL_HOST: 127.0.0.1
15
+ MINI_SQL_MYSQL_PORT: 3306
16
+ MINI_SQL_MYSQL_PASSWORD: mysql
17
+ jobs:
18
+ build:
19
+ runs-on: ubuntu-latest
20
+ name: Ruby ${{ matrix.ruby }}
21
+ services:
22
+ postgres:
23
+ image: postgres:9.6
24
+ env:
25
+ POSTGRES_PASSWORD: postgres
26
+ ports:
27
+ - 5432:5432
28
+ options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
29
+ mysql:
30
+ image: mysql:5.7
31
+ env:
32
+ MYSQL_ROOT_PASSWORD: mysql
33
+ ports:
34
+ - 3306:3306
35
+ options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
36
+ strategy:
37
+ matrix:
38
+ ruby: ["2.7", "2.6", "2.5"]
39
+ experimental: [false]
40
+ include:
41
+ - ruby: ruby-head
42
+ experimental: true
43
+ steps:
44
+ - uses: actions/checkout@v2
45
+ - uses: ruby/setup-ruby@v1
46
+ with:
47
+ ruby-version: ${{ matrix.ruby }}
48
+ - name: Bundler cache
49
+ uses: actions/cache@v2
50
+ with:
51
+ path: vendor/bundle
52
+ key: ${{ runner.os }}-${{ matrix.ruby }}-gems-${{ hashFiles('**/Gemfile.lock') }}
53
+ restore-keys: |
54
+ ${{ runner.os }}-${{ matrix.ruby }}-gems-
55
+ - name: Create Databases
56
+ run: |
57
+ createdb test_mini_sql
58
+ mysql --host=127.0.0.1 --port=3306 --user=root --password=mysql -e 'CREATE DATABASE test_mini_sql'
59
+ - name: Setup gems
60
+ run: |
61
+ bundle config path vendor/bundle
62
+ bundle install --jobs 4
63
+ - name: Tests
64
+ run: bundle exec rake test
65
+ - name: Rubocop
66
+ run: bundle exec rubocop
@@ -1,5 +1,8 @@
1
- inherit_from: https://raw.githubusercontent.com/discourse/discourse/master/.rubocop.yml
2
-
1
+ inherit_gem:
2
+ rubocop-discourse: default.yml
3
+ inherit_mode:
4
+ merge:
5
+ - Exclude
3
6
  AllCops:
4
7
  Exclude:
5
8
  - 'bench/**/*'
data/CHANGELOG CHANGED
@@ -1,3 +1,9 @@
1
+ 2020-12-30 - 1.0
2
+
3
+ - Added serialization support using MiniSql::Serializer.to_json / .from_json
4
+ - Fixed minor issue with cache poisoning when using query_decorator
5
+ - Version 1.0 to reflect the stability of the interfaces and project, used in productions for almost 2 years now
6
+
1
7
  2020-06-25 - 0.3
2
8
 
3
9
  - Added support for query_each and query_each_hash, which lazily queries rows and enables selecting large result sets by streaming
@@ -8,6 +8,9 @@ require_relative "mini_sql/connection"
8
8
  require_relative "mini_sql/deserializer_cache"
9
9
  require_relative "mini_sql/builder"
10
10
  require_relative "mini_sql/inline_param_encoder"
11
+ require_relative "mini_sql/decoratable"
12
+ require_relative "mini_sql/serializer"
13
+ require_relative "mini_sql/result"
11
14
 
12
15
  module MiniSql
13
16
  if RUBY_ENGINE == 'jruby'
@@ -66,4 +66,14 @@ class MiniSql::Builder
66
66
  RUBY
67
67
  end
68
68
 
69
+ def query_decorator(decorator, hash_args = nil)
70
+ hash_args = @args.merge(hash_args) if hash_args && @args
71
+ hash_args ||= @args
72
+ if hash_args
73
+ @connection.query_decorator(decorator, to_sql, hash_args)
74
+ else
75
+ @connection.query_decorator(decorator, to_sql)
76
+ end
77
+ end
78
+
69
79
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MiniSql
4
+ module Decoratable
5
+ def decorated(mod)
6
+ @decoratorated_classes ||= {}
7
+ @decoratorated_classes[mod] ||=
8
+ Class.new(self) do
9
+ include(mod)
10
+ instance_eval <<~RUBY
11
+ def decorator
12
+ #{mod}
13
+ end
14
+ RUBY
15
+ end
16
+ end
17
+
18
+ def decorator
19
+ nil
20
+ end
21
+ end
22
+ end
@@ -50,9 +50,7 @@ module MiniSql
50
50
  private
51
51
 
52
52
  def run(sql, as, params)
53
- if params && params.length > 0
54
- sql = param_encoder.encode(sql, *params)
55
- end
53
+ sql = param_encoder.encode(sql, *params)
56
54
  raw_connection.query(
57
55
  sql,
58
56
  as: as,
@@ -23,7 +23,9 @@ module MiniSql
23
23
  @cache.shift if @cache.length > @max_size
24
24
  end
25
25
 
26
- materializer.include(decorator_module) if decorator_module
26
+ if decorator_module
27
+ materializer = materializer.decorated(decorator_module)
28
+ end
27
29
 
28
30
  result.map do |data|
29
31
  materializer.materialize(data)
@@ -36,18 +38,10 @@ module MiniSql
36
38
  fields = result.fields
37
39
 
38
40
  Class.new do
39
- attr_accessor(*fields)
40
-
41
- # AM serializer support
42
- alias :read_attribute_for_serialization :send
41
+ extend MiniSql::Decoratable
42
+ include MiniSql::Result
43
43
 
44
- def to_h
45
- r = {}
46
- instance_variables.each do |f|
47
- r[f.to_s.sub('@', '').to_sym] = instance_variable_get(f)
48
- end
49
- r
50
- end
44
+ attr_accessor(*fields)
51
45
 
52
46
  instance_eval <<~RUBY
53
47
  def materialize(data)
@@ -84,7 +84,7 @@ module MiniSql
84
84
  result.type_map = type_map
85
85
  result.values
86
86
  ensure
87
- result.clear if result
87
+ result.clear if result
88
88
  end
89
89
 
90
90
  def query(sql, *params)
@@ -97,9 +97,7 @@ module MiniSql
97
97
 
98
98
  def query_each(sql, *params)
99
99
  raise StandardError, "Please supply a block when calling query_each" if !block_given?
100
- if params && params.length > 0
101
- sql = param_encoder.encode(sql, *params)
102
- end
100
+ sql = param_encoder.encode(sql, *params)
103
101
 
104
102
  raw_connection.send_query(sql)
105
103
  raw_connection.set_single_row_mode
@@ -130,9 +128,7 @@ module MiniSql
130
128
 
131
129
  def query_each_hash(sql, *params)
132
130
  raise StandardError, "Please supply a block when calling query_each_hash" if !block_given?
133
- if params && params.length > 0
134
- sql = param_encoder.encode(sql, *params)
135
- end
131
+ sql = param_encoder.encode(sql, *params)
136
132
 
137
133
  raw_connection.send_query(sql)
138
134
  raw_connection.set_single_row_mode
@@ -195,9 +191,7 @@ module MiniSql
195
191
  private
196
192
 
197
193
  def run(sql, params)
198
- if params && params.length > 0
199
- sql = param_encoder.encode(sql, *params)
200
- end
194
+ sql = param_encoder.encode(sql, *params)
201
195
  raw_connection.async_exec(sql)
202
196
  end
203
197
 
@@ -39,7 +39,9 @@ module MiniSql
39
39
  @cache.shift if @cache.length > @max_size
40
40
  end
41
41
 
42
- materializer.include(decorator_module) if decorator_module
42
+ if decorator_module
43
+ materializer = materializer.decorated(decorator_module)
44
+ end
43
45
 
44
46
  i = 0
45
47
  r = []
@@ -66,18 +68,10 @@ module MiniSql
66
68
  end
67
69
 
68
70
  Class.new do
69
- attr_accessor(*fields)
70
-
71
- # AM serializer support
72
- alias :read_attribute_for_serialization :send
71
+ extend MiniSql::Decoratable
72
+ include MiniSql::Result
73
73
 
74
- def to_h
75
- r = {}
76
- instance_variables.each do |f|
77
- r[f.to_s.sub('@', '').to_sym] = instance_variable_get(f)
78
- end
79
- r
80
- end
74
+ attr_accessor(*fields)
81
75
 
82
76
  instance_eval <<~RUBY
83
77
  def materialize(pg_result, index)
@@ -89,7 +89,7 @@ module MiniSql
89
89
  private
90
90
 
91
91
  def run(sql, params)
92
- sql = param_encoder.encode(sql, *params) if params && params.length > 0
92
+ sql = param_encoder.encode(sql, *params)
93
93
  conn = raw_connection
94
94
  conn.typemap = self.class.typemap
95
95
  conn.execute(sql)
@@ -28,6 +28,10 @@ module MiniSql
28
28
 
29
29
  materializer.include(decorator_module) if decorator_module
30
30
 
31
+ if decorator_module
32
+ materializer = materializer.decorated(decorator_module)
33
+ end
34
+
31
35
  i = 0
32
36
  r = []
33
37
  # quicker loop
@@ -44,18 +48,10 @@ module MiniSql
44
48
  fields = result.fields
45
49
 
46
50
  Class.new do
47
- attr_accessor(*fields)
51
+ extend MiniSql::Decoratable
52
+ include MiniSql::Result
48
53
 
49
- # AM serializer support
50
- alias :read_attribute_for_serialization :send
51
-
52
- def to_h
53
- r = {}
54
- instance_variables.each do |f|
55
- r[f.to_s.sub('@', '').to_sym] = instance_variable_get(f)
56
- end
57
- r
58
- end
54
+ attr_accessor(*fields)
59
55
 
60
56
  instance_eval <<~RUBY
61
57
  def materialize(pg_result, index)
@@ -0,0 +1,20 @@
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
+ end
20
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MiniSql
4
+ module Serializer
5
+ MAX_CACHE_SIZE = 500
6
+
7
+ def self.to_json(result)
8
+ wrapper =
9
+ if result.length == 0
10
+ {}
11
+ else
12
+ {
13
+ "decorator" => result[0].class.decorator.to_s,
14
+ "fields" => result[0].to_h.keys,
15
+ "data" => result.map(&:values),
16
+ }
17
+ end
18
+
19
+ JSON.generate(wrapper)
20
+ end
21
+
22
+ def self.from_json(json)
23
+ wrapper = JSON.parse(json)
24
+ if !wrapper["data"]
25
+ []
26
+ else
27
+ materializer = cached_materializer(wrapper['fields'], wrapper['decorator'])
28
+ wrapper["data"].map do |row|
29
+ materializer.materialize(row)
30
+ end
31
+ end
32
+ end
33
+
34
+ def self.cached_materializer(fields, decorator_module = nil)
35
+ @cache ||= {}
36
+ key = fields
37
+ m = @cache.delete(key)
38
+ if m
39
+ @cache[key] = m
40
+ else
41
+ m = @cache[key] = materializer(fields)
42
+ @cache.shift if @cache.length > MAX_CACHE_SIZE
43
+ end
44
+
45
+ if decorator_module && decorator_module.length > 0
46
+ decorator = Kernel.const_get(decorator_module)
47
+ m = m.decorated(decorator)
48
+ end
49
+
50
+ m
51
+ end
52
+
53
+ def self.materializer(fields)
54
+ Class.new do
55
+ extend MiniSql::Decoratable
56
+ include MiniSql::Result
57
+
58
+ attr_accessor(*fields)
59
+
60
+ instance_eval <<~RUBY
61
+ def materialize(values)
62
+ r = self.new
63
+ #{col = -1; fields.map { |f| "r.#{f} = values[#{col += 1}]" }.join("; ")}
64
+ r
65
+ end
66
+ RUBY
67
+ end
68
+ end
69
+ end
70
+ end
@@ -63,9 +63,7 @@ module MiniSql
63
63
  private
64
64
 
65
65
  def run(sql, *params)
66
- if params && params.length > 0
67
- sql = param_encoder.encode(sql, *params)
68
- end
66
+ sql = param_encoder.encode(sql, *params)
69
67
  if block_given?
70
68
  stmt = SQLite3::Statement.new(raw_connection, sql)
71
69
  yield stmt.execute
@@ -24,7 +24,9 @@ module MiniSql
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
@@ -43,18 +45,10 @@ module MiniSql
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)
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module MiniSql
3
- VERSION = "0.3"
3
+ VERSION = "1.0"
4
4
  end
@@ -25,11 +25,11 @@ 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"
@@ -38,8 +38,9 @@ Gem::Specification.new do |spec|
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.3'
4
+ version: '1.0'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Saffron
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-25 00:00:00.000000000 Z
11
+ date: 2020-12-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -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
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
- version: 1.0.2
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
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
@@ -190,6 +204,7 @@ files:
190
204
  - lib/mini_sql.rb
191
205
  - lib/mini_sql/builder.rb
192
206
  - lib/mini_sql/connection.rb
207
+ - lib/mini_sql/decoratable.rb
193
208
  - lib/mini_sql/deserializer_cache.rb
194
209
  - lib/mini_sql/inline_param_encoder.rb
195
210
  - lib/mini_sql/mysql/connection.rb
@@ -199,6 +214,8 @@ files:
199
214
  - lib/mini_sql/postgres/deserializer_cache.rb
200
215
  - lib/mini_sql/postgres_jdbc/connection.rb
201
216
  - lib/mini_sql/postgres_jdbc/deserializer_cache.rb
217
+ - lib/mini_sql/result.rb
218
+ - lib/mini_sql/serializer.rb
202
219
  - lib/mini_sql/sqlite/connection.rb
203
220
  - lib/mini_sql/sqlite/deserializer_cache.rb
204
221
  - lib/mini_sql/version.rb
@@ -225,7 +242,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
225
242
  - !ruby/object:Gem::Version
226
243
  version: '0'
227
244
  requirements: []
228
- rubygems_version: 3.0.3
245
+ rubygems_version: 3.2.3
229
246
  signing_key:
230
247
  specification_version: 4
231
248
  summary: A fast, safe, simple direct SQL executor
@@ -1,28 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 2.5
4
- - 2.6
5
- - 2.7
6
- - ruby-head
7
-
8
- before_install:
9
- - gem install bundler
10
-
11
- cache: bundler
12
- sudo: false
13
-
14
- services:
15
- - mysql
16
-
17
- addons:
18
- postgresql: 9.6
19
- mysql: 5.7
20
-
21
- install:
22
- - createdb test_mini_sql
23
- - mysql -e 'CREATE DATABASE test_mini_sql;'
24
- - bundle install
25
-
26
- matrix:
27
- allow_failures:
28
- - rvm: ruby-head