mini_sql 0.2.2 → 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.
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MiniSql
4
+ module Mysql
5
+ class DeserializerCache
6
+
7
+ DEFAULT_MAX_SIZE = 500
8
+
9
+ def initialize(max_size = nil)
10
+ @cache = {}
11
+ @max_size = max_size || DEFAULT_MAX_SIZE
12
+ end
13
+
14
+ def materialize(result, decorator_module = nil)
15
+ key = result.fields
16
+
17
+ # trivial fast LRU implementation
18
+ materializer = @cache.delete(key)
19
+ if materializer
20
+ @cache[key] = materializer
21
+ else
22
+ materializer = @cache[key] = new_row_matrializer(result)
23
+ @cache.shift if @cache.length > @max_size
24
+ end
25
+
26
+ if decorator_module
27
+ materializer = materializer.decorated(decorator_module)
28
+ end
29
+
30
+ result.map do |data|
31
+ materializer.materialize(data)
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def new_row_matrializer(result)
38
+ fields = result.fields
39
+
40
+ Class.new do
41
+ extend MiniSql::Decoratable
42
+ include MiniSql::Result
43
+
44
+ attr_accessor(*fields)
45
+
46
+ instance_eval <<~RUBY
47
+ def materialize(data)
48
+ r = self.new
49
+ #{col = -1; fields.map { |f| "r.#{f} = data[#{col += 1}]" }.join("; ")}
50
+ r
51
+ end
52
+ RUBY
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module MiniSql
2
4
  module Postgres
3
5
  module Coders
@@ -39,6 +39,7 @@ module MiniSql
39
39
  @raw_connection = raw_connection
40
40
  @deserializer_cache = (args && args[:deserializer_cache]) || self.class.default_deserializer_cache
41
41
  @param_encoder = (args && args[:param_encoder]) || InlineParamEncoder.new(self)
42
+ @type_map = args && args[:type_map]
42
43
  end
43
44
 
44
45
  def type_map
@@ -78,6 +79,14 @@ module MiniSql
78
79
  result.clear if result
79
80
  end
80
81
 
82
+ def query_array(sql, *params)
83
+ result = run(sql, params)
84
+ result.type_map = type_map
85
+ result.values
86
+ ensure
87
+ result.clear if result
88
+ end
89
+
81
90
  def query(sql, *params)
82
91
  result = run(sql, params)
83
92
  result.type_map = type_map
@@ -86,6 +95,76 @@ module MiniSql
86
95
  result.clear if result
87
96
  end
88
97
 
98
+ def query_each(sql, *params)
99
+ raise StandardError, "Please supply a block when calling query_each" if !block_given?
100
+ sql = param_encoder.encode(sql, *params)
101
+
102
+ raw_connection.send_query(sql)
103
+ raw_connection.set_single_row_mode
104
+
105
+ loop do
106
+ result = raw_connection.get_result
107
+ break if !result
108
+
109
+ result.check
110
+
111
+ if result.ntuples == 0
112
+ # skip, this happens at the end when we get totals
113
+ else
114
+ materializer ||= @deserializer_cache.materializer(result)
115
+ result.type_map = type_map
116
+ i = 0
117
+ # technically we should only get 1 row here
118
+ # but protect against future batching changes
119
+ while i < result.ntuples
120
+ yield materializer.materialize(result, i)
121
+ i += 1
122
+ end
123
+ end
124
+
125
+ result.clear
126
+ end
127
+ end
128
+
129
+ def query_each_hash(sql, *params)
130
+ raise StandardError, "Please supply a block when calling query_each_hash" if !block_given?
131
+ sql = param_encoder.encode(sql, *params)
132
+
133
+ raw_connection.send_query(sql)
134
+ raw_connection.set_single_row_mode
135
+
136
+ loop do
137
+ result = raw_connection.get_result
138
+ break if !result
139
+
140
+ result.check
141
+
142
+ if result.ntuples == 0
143
+ # skip, this happens at the end when we get totals
144
+ else
145
+ result.type_map = type_map
146
+ i = 0
147
+
148
+ # technically we should only get 1 row here
149
+ # but protect against future batching changes
150
+ while i < result.ntuples
151
+ yield result[i]
152
+ i += 1
153
+ end
154
+ end
155
+
156
+ result.clear
157
+ end
158
+ end
159
+
160
+ def query_decorator(decorator, sql, *params)
161
+ result = run(sql, params)
162
+ result.type_map = type_map
163
+ @deserializer_cache.materialize(result, decorator)
164
+ ensure
165
+ result.clear if result
166
+ end
167
+
89
168
  def exec(sql, *params)
90
169
  result = run(sql, params)
91
170
  result.cmd_tuples
@@ -112,9 +191,7 @@ module MiniSql
112
191
  private
113
192
 
114
193
  def run(sql, params)
115
- if params && params.length > 0
116
- sql = param_encoder.encode(sql, *params)
117
- end
194
+ sql = param_encoder.encode(sql, *params)
118
195
  raw_connection.async_exec(sql)
119
196
  end
120
197
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module MiniSql
2
4
  module Postgres
3
5
  class DeserializerCache
@@ -9,8 +11,21 @@ module MiniSql
9
11
  @max_size = max_size || DEFAULT_MAX_SIZE
10
12
  end
11
13
 
12
- def materialize(result)
14
+ def materializer(result)
15
+ key = result.fields
16
+
17
+ materializer = @cache.delete(key)
18
+ if materializer
19
+ @cache[key] = materializer
20
+ else
21
+ materializer = @cache[key] = new_row_matrializer(result)
22
+ @cache.shift if @cache.length > @max_size
23
+ end
24
+
25
+ materializer
26
+ end
13
27
 
28
+ def materialize(result, decorator_module = nil)
14
29
  return [] if result.ntuples == 0
15
30
 
16
31
  key = result.fields
@@ -24,6 +39,10 @@ module MiniSql
24
39
  @cache.shift if @cache.length > @max_size
25
40
  end
26
41
 
42
+ if decorator_module
43
+ materializer = materializer.decorated(decorator_module)
44
+ end
45
+
27
46
  i = 0
28
47
  r = []
29
48
  # quicker loop
@@ -39,24 +58,25 @@ module MiniSql
39
58
  def new_row_matrializer(result)
40
59
  fields = result.fields
41
60
 
42
- Class.new do
43
- attr_accessor(*fields)
61
+ i = 0
62
+ while i < fields.length
63
+ # special handling for unamed column
64
+ if fields[i] == "?column?"
65
+ fields[i] = "column#{i}"
66
+ end
67
+ i += 1
68
+ end
44
69
 
45
- # AM serializer support
46
- alias :read_attribute_for_serialization :send
70
+ Class.new do
71
+ extend MiniSql::Decoratable
72
+ include MiniSql::Result
47
73
 
48
- def to_h
49
- r = {}
50
- instance_variables.each do |f|
51
- r[f.to_s.sub('@','').to_sym] = instance_variable_get(f)
52
- end
53
- r
54
- end
74
+ attr_accessor(*fields)
55
75
 
56
76
  instance_eval <<~RUBY
57
77
  def materialize(pg_result, index)
58
78
  r = self.new
59
- #{col=-1; fields.map{|f| "r.#{f} = pg_result.getvalue(index, #{col+=1})"}.join("; ")}
79
+ #{col = -1; fields.map { |f| "r.#{f} = pg_result.getvalue(index, #{col += 1})" }.join("; ")}
60
80
  r
61
81
  end
62
82
  RUBY
@@ -60,6 +60,11 @@ module MiniSql
60
60
  @deserializer_cache.materialize(result)
61
61
  end
62
62
 
63
+ def query_decorator(decorator, sql, *params)
64
+ result = run(sql, params)
65
+ @deserializer_cache.materialize(result, decorator)
66
+ end
67
+
63
68
  def exec(sql, *params)
64
69
  result = run(sql, params)
65
70
  if result.kind_of? Integer
@@ -84,7 +89,7 @@ module MiniSql
84
89
  private
85
90
 
86
91
  def run(sql, params)
87
- sql = param_encoder.encode(sql, *params) if params && params.length > 0
92
+ sql = param_encoder.encode(sql, *params)
88
93
  conn = raw_connection
89
94
  conn.typemap = self.class.typemap
90
95
  conn.execute(sql)
@@ -1,67 +1,67 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module MiniSql
2
4
  module Postgres
3
5
  class DeserializerCache
4
6
 
5
- DEFAULT_MAX_SIZE = 500
7
+ DEFAULT_MAX_SIZE = 500
6
8
 
7
- def initialize(max_size = nil)
8
- @cache = {}
9
- @max_size = max_size || DEFAULT_MAX_SIZE
10
- end
9
+ def initialize(max_size = nil)
10
+ @cache = {}
11
+ @max_size = max_size || DEFAULT_MAX_SIZE
12
+ end
11
13
 
12
- def materialize(result)
14
+ def materialize(result, decorator_module = nil)
13
15
 
14
- return [] if result.ntuples == 0
16
+ return [] if result.ntuples == 0
15
17
 
16
- key = result.fields
18
+ key = result.fields
17
19
 
18
- # trivial fast LRU implementation
19
- materializer = @cache.delete(key)
20
- if materializer
21
- @cache[key] = materializer
22
- else
23
- materializer = @cache[key] = new_row_matrializer(result)
24
- @cache.shift if @cache.length > @max_size
25
- end
20
+ # trivial fast LRU implementation
21
+ materializer = @cache.delete(key)
22
+ if materializer
23
+ @cache[key] = materializer
24
+ else
25
+ materializer = @cache[key] = new_row_matrializer(result)
26
+ @cache.shift if @cache.length > @max_size
27
+ end
26
28
 
27
- i = 0
28
- r = []
29
- # quicker loop
30
- while i < result.ntuples
31
- r << materializer.materialize(result, i)
32
- i += 1
33
- end
34
- r
35
- end
29
+ materializer.include(decorator_module) if decorator_module
36
30
 
37
- private
31
+ if decorator_module
32
+ materializer = materializer.decorated(decorator_module)
33
+ end
34
+
35
+ i = 0
36
+ r = []
37
+ # quicker loop
38
+ while i < result.ntuples
39
+ r << materializer.materialize(result, i)
40
+ i += 1
41
+ end
42
+ r
43
+ end
38
44
 
39
- def new_row_matrializer(result)
40
- fields = result.fields
45
+ private
41
46
 
42
- Class.new do
43
- attr_accessor(*fields)
47
+ def new_row_matrializer(result)
48
+ fields = result.fields
44
49
 
45
- # AM serializer support
46
- alias :read_attribute_for_serialization :send
50
+ Class.new do
51
+ extend MiniSql::Decoratable
52
+ include MiniSql::Result
47
53
 
48
- def to_h
49
- r = {}
50
- instance_variables.each do |f|
51
- r[f.to_s.sub('@','').to_sym] = instance_variable_get(f)
52
- end
53
- r
54
- end
54
+ attr_accessor(*fields)
55
55
 
56
- instance_eval <<~RUBY
56
+ instance_eval <<~RUBY
57
57
  def materialize(pg_result, index)
58
58
  r = self.new
59
- #{col=-1; fields.map{|f| "r.#{f} = pg_result.getvalue(index, #{col+=1})"}.join("; ")}
59
+ #{col = -1; fields.map { |f| "r.#{f} = pg_result.getvalue(index, #{col += 1})" }.join("; ")}
60
60
  r
61
61
  end
62
- RUBY
62
+ RUBY
63
+ end
63
64
  end
64
65
  end
65
66
  end
66
67
  end
67
- end
@@ -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