mini_sql 0.2.1 → 0.3

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,63 @@
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
+ materializer.include(decorator_module) if decorator_module
27
+
28
+ result.map do |data|
29
+ materializer.materialize(data)
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def new_row_matrializer(result)
36
+ fields = result.fields
37
+
38
+ Class.new do
39
+ attr_accessor(*fields)
40
+
41
+ # AM serializer support
42
+ alias :read_attribute_for_serialization :send
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
51
+
52
+ instance_eval <<~RUBY
53
+ def materialize(data)
54
+ r = self.new
55
+ #{col = -1; fields.map { |f| "r.#{f} = data[#{col += 1}]" }.join("; ")}
56
+ r
57
+ end
58
+ RUBY
59
+ end
60
+ end
61
+ end
62
+ end
63
+ 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,80 @@ 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
+ if params && params.length > 0
101
+ sql = param_encoder.encode(sql, *params)
102
+ end
103
+
104
+ raw_connection.send_query(sql)
105
+ raw_connection.set_single_row_mode
106
+
107
+ loop do
108
+ result = raw_connection.get_result
109
+ break if !result
110
+
111
+ result.check
112
+
113
+ if result.ntuples == 0
114
+ # skip, this happens at the end when we get totals
115
+ else
116
+ materializer ||= @deserializer_cache.materializer(result)
117
+ result.type_map = type_map
118
+ i = 0
119
+ # technically we should only get 1 row here
120
+ # but protect against future batching changes
121
+ while i < result.ntuples
122
+ yield materializer.materialize(result, i)
123
+ i += 1
124
+ end
125
+ end
126
+
127
+ result.clear
128
+ end
129
+ end
130
+
131
+ def query_each_hash(sql, *params)
132
+ 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
136
+
137
+ raw_connection.send_query(sql)
138
+ raw_connection.set_single_row_mode
139
+
140
+ loop do
141
+ result = raw_connection.get_result
142
+ break if !result
143
+
144
+ result.check
145
+
146
+ if result.ntuples == 0
147
+ # skip, this happens at the end when we get totals
148
+ else
149
+ result.type_map = type_map
150
+ i = 0
151
+
152
+ # technically we should only get 1 row here
153
+ # but protect against future batching changes
154
+ while i < result.ntuples
155
+ yield result[i]
156
+ i += 1
157
+ end
158
+ end
159
+
160
+ result.clear
161
+ end
162
+ end
163
+
164
+ def query_decorator(decorator, sql, *params)
165
+ result = run(sql, params)
166
+ result.type_map = type_map
167
+ @deserializer_cache.materialize(result, decorator)
168
+ ensure
169
+ result.clear if result
170
+ end
171
+
89
172
  def exec(sql, *params)
90
173
  result = run(sql, params)
91
174
  result.cmd_tuples
@@ -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,8 @@ module MiniSql
24
39
  @cache.shift if @cache.length > @max_size
25
40
  end
26
41
 
42
+ materializer.include(decorator_module) if decorator_module
43
+
27
44
  i = 0
28
45
  r = []
29
46
  # quicker loop
@@ -39,6 +56,15 @@ module MiniSql
39
56
  def new_row_matrializer(result)
40
57
  fields = result.fields
41
58
 
59
+ i = 0
60
+ while i < fields.length
61
+ # special handling for unamed column
62
+ if fields[i] == "?column?"
63
+ fields[i] = "column#{i}"
64
+ end
65
+ i += 1
66
+ end
67
+
42
68
  Class.new do
43
69
  attr_accessor(*fields)
44
70
 
@@ -48,7 +74,7 @@ module MiniSql
48
74
  def to_h
49
75
  r = {}
50
76
  instance_variables.each do |f|
51
- r[f.to_s.sub('@','').to_sym] = instance_variable_get(f)
77
+ r[f.to_s.sub('@', '').to_sym] = instance_variable_get(f)
52
78
  end
53
79
  r
54
80
  end
@@ -56,7 +82,7 @@ module MiniSql
56
82
  instance_eval <<~RUBY
57
83
  def materialize(pg_result, index)
58
84
  r = self.new
59
- #{col=-1; fields.map{|f| "r.#{f} = pg_result.getvalue(index, #{col+=1})"}.join("; ")}
85
+ #{col = -1; fields.map { |f| "r.#{f} = pg_result.getvalue(index, #{col += 1})" }.join("; ")}
60
86
  r
61
87
  end
62
88
  RUBY
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MiniSql
4
+ module Postgres
5
+ class Connection < MiniSql::Connection
6
+ class NumericCoder
7
+ def decode(string)
8
+ BigDecimal(string)
9
+ end
10
+ end
11
+
12
+ class IPAddrCoder
13
+ def decode(string)
14
+ IPAddr.new(string)
15
+ end
16
+ end
17
+
18
+ attr_reader :raw_connection, :type_map, :param_encoder
19
+
20
+ def self.default_deserializer_cache
21
+ @deserializer_cache ||= DeserializerCache.new
22
+ end
23
+
24
+ def self.typemap
25
+ @type_map ||= {
26
+ "numeric" => NumericCoder.new,
27
+ "inet" => IPAddrCoder.new,
28
+ "cidr" => IPAddrCoder.new
29
+ }
30
+ end
31
+
32
+ # Initialize a new MiniSql::Postgres::Connection object
33
+ #
34
+ # @param raw_connection [PG::Connection] an active connection to PG
35
+ # @param deserializer_cache [MiniSql::DeserializerCache] a cache of field names to deserializer, can be nil
36
+ # @param type_map [PG::TypeMap] a type mapper for all results returned, can be nil
37
+ def initialize(raw_connection, args = nil)
38
+ @raw_connection = raw_connection
39
+ @deserializer_cache = (args && args[:deserializer_cache]) || self.class.default_deserializer_cache
40
+ @param_encoder = (args && args[:param_encoder]) || InlineParamEncoder.new(self)
41
+ end
42
+
43
+ # Returns a flat array containing all results.
44
+ # Note, if selecting multiple columns array will be flattened
45
+ #
46
+ # @param sql [String] the query to run
47
+ # @param params [Array or Hash], params to apply to query
48
+ # @return [Object] a flat array containing all results
49
+ def query_single(sql, *params)
50
+ result = run(sql, params)
51
+ if result.length == 1
52
+ result.values[0]
53
+ else
54
+ result.values.each_with_object([]) { |value, array| array.concat value }
55
+ end
56
+ end
57
+
58
+ def query(sql, *params)
59
+ result = run(sql, params)
60
+ @deserializer_cache.materialize(result)
61
+ end
62
+
63
+ def query_decorator(decorator, sql, *params)
64
+ result = run(sql, params)
65
+ @deserializer_cache.materialize(result, decorator)
66
+ end
67
+
68
+ def exec(sql, *params)
69
+ result = run(sql, params)
70
+ if result.kind_of? Integer
71
+ result
72
+ else
73
+ result.length
74
+ end
75
+ end
76
+
77
+ def query_hash(sql, *params)
78
+ run(sql, params).to_a
79
+ end
80
+
81
+ def build(sql)
82
+ Builder.new(self, sql)
83
+ end
84
+
85
+ def escape_string(str)
86
+ raw_connection.escape_string(str)
87
+ end
88
+
89
+ private
90
+
91
+ def run(sql, params)
92
+ sql = param_encoder.encode(sql, *params) if params && params.length > 0
93
+ conn = raw_connection
94
+ conn.typemap = self.class.typemap
95
+ conn.execute(sql)
96
+ ensure
97
+ # Force unsetting of typemap since we don't want mixed AR usage to continue to use these extra converters.
98
+ conn.typemap = nil
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MiniSql
4
+ module Postgres
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
+
16
+ return [] if result.ntuples == 0
17
+
18
+ key = result.fields
19
+
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
28
+
29
+ materializer.include(decorator_module) if decorator_module
30
+
31
+ i = 0
32
+ r = []
33
+ # quicker loop
34
+ while i < result.ntuples
35
+ r << materializer.materialize(result, i)
36
+ i += 1
37
+ end
38
+ r
39
+ end
40
+
41
+ private
42
+
43
+ def new_row_matrializer(result)
44
+ fields = result.fields
45
+
46
+ Class.new do
47
+ attr_accessor(*fields)
48
+
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
59
+
60
+ instance_eval <<~RUBY
61
+ def materialize(pg_result, index)
62
+ r = self.new
63
+ #{col = -1; fields.map { |f| "r.#{f} = pg_result.getvalue(index, #{col += 1})" }.join("; ")}
64
+ r
65
+ end
66
+ RUBY
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -26,6 +26,10 @@ module MiniSql
26
26
  r
27
27
  end
28
28
 
29
+ def query_array(sql, *params)
30
+ run(sql, *params)
31
+ end
32
+
29
33
  def exec(sql, *params)
30
34
 
31
35
  start = raw_connection.total_changes
@@ -46,8 +50,14 @@ module MiniSql
46
50
  end
47
51
  end
48
52
 
53
+ def query_decorator(decorator, sql, *params)
54
+ run(sql, *params) do |set|
55
+ deserializer_cache.materialize(set, decorator)
56
+ end
57
+ end
58
+
49
59
  def escape_string(str)
50
- str.gsub("'","''")
60
+ str.gsub("'", "''")
51
61
  end
52
62
 
53
63
  private
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module MiniSql
2
4
  module Sqlite
3
5
  class DeserializerCache
@@ -9,7 +11,7 @@ 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
16
  key = result.columns
15
17
 
@@ -22,6 +24,8 @@ module MiniSql
22
24
  @cache.shift if @cache.length > @max_size
23
25
  end
24
26
 
27
+ materializer.include(decorator_module) if decorator_module
28
+
25
29
  r = []
26
30
  # quicker loop
27
31
  while !result.eof?
@@ -47,7 +51,7 @@ module MiniSql
47
51
  def to_h
48
52
  r = {}
49
53
  instance_variables.each do |f|
50
- r[f.to_s.sub('@','').to_sym] = instance_variable_get(f)
54
+ r[f.to_s.sub('@', '').to_sym] = instance_variable_get(f)
51
55
  end
52
56
  r
53
57
  end
@@ -55,7 +59,7 @@ module MiniSql
55
59
  instance_eval <<~RUBY
56
60
  def materialize(data)
57
61
  r = self.new
58
- #{col=-1; fields.map{|f| "r.#{f} = data[#{col+=1}]"}.join("; ")}
62
+ #{col = -1; fields.map { |f| "r.#{f} = data[#{col += 1}]" }.join("; ")}
59
63
  r
60
64
  end
61
65
  RUBY