mini_sql 0.2.2 → 1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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