mini_sql 0.2.3 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require "bundler/setup"
4
5
  require "mini_sql"
@@ -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,5 +66,14 @@ class MiniSql::Builder
66
66
  RUBY
67
67
  end
68
68
 
69
- end
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
70
78
 
79
+ end
@@ -4,7 +4,7 @@ module MiniSql
4
4
  class Connection
5
5
 
6
6
  def self.get(raw_connection, options = {})
7
- if (defined? ::PG::Connection) && (PG::Connection === raw_connection)
7
+ if (defined? ::PG::Connection) && (PG::Connection === raw_connection)
8
8
  Postgres::Connection.new(raw_connection, options)
9
9
  elsif (defined? ::ArJdbc)
10
10
  Postgres::Connection.new(raw_connection, options)
@@ -31,11 +31,23 @@ module MiniSql
31
31
  raise NotImplementedError, "must be implemented by child connection"
32
32
  end
33
33
 
34
- def exec(sql, *params)
34
+ def query_hash(sql, *params)
35
35
  raise NotImplementedError, "must be implemented by child connection"
36
36
  end
37
37
 
38
- def query_hash(sql, *params)
38
+ def query_decorator(sql, *params)
39
+ raise NotImplementedError, "must be implemented by child connection"
40
+ end
41
+
42
+ def query_each(sql, *params)
43
+ raise NotImplementedError, "must be implemented by child connection"
44
+ end
45
+
46
+ def query_each_hash(sql, *params)
47
+ raise NotImplementedError, "must be implemented by child connection"
48
+ end
49
+
50
+ def exec(sql, *params)
39
51
  raise NotImplementedError, "must be implemented by child connection"
40
52
  end
41
53
 
@@ -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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module MiniSql
2
4
  class DeserializerCache
3
5
  # method takes a raw result and converts to proper objects
@@ -9,8 +9,6 @@ module MiniSql
9
9
  end
10
10
 
11
11
  def encode(sql, *params)
12
- return sql unless params && params.length > 0
13
-
14
12
  if Hash === (hash = params[0])
15
13
  raise ArgumentError, "Only one hash param is allowed, multiple were sent" if params.length > 1
16
14
  encode_hash(sql, hash)
@@ -50,19 +48,19 @@ module MiniSql
50
48
 
51
49
  def quote_val(value)
52
50
  case value
51
+ when String then "'#{conn.escape_string(value.to_s)}'"
52
+ when Numeric then value.to_s
53
+ when BigDecimal then value.to_s("F")
54
+ when Date, Time then "'#{quoted_date(value)}'"
55
+ when Symbol then "'#{conn.escape_string(value.to_s)}'"
56
+ when true then "true"
57
+ when false then "false"
58
+ when nil then "NULL"
59
+ when [] then "NULL"
53
60
  when Array
54
61
  value.map do |v|
55
62
  quote_val(v)
56
63
  end.join(', ')
57
- when String
58
- "'#{conn.escape_string(value.to_s)}'"
59
- when true then "true"
60
- when false then "false"
61
- when nil then "NULL"
62
- when BigDecimal then value.to_s("F")
63
- when Numeric then value.to_s
64
- when Date, Time then "'#{quoted_date(value)}'"
65
- when Symbol then "'#{escape_string(value.to_s)}'"
66
64
  else raise TypeError, "can't quote #{value.class.name}"
67
65
  end
68
66
  end
@@ -20,6 +20,10 @@ module MiniSql
20
20
  result.to_a
21
21
  end
22
22
 
23
+ def query_array(sql, *params)
24
+ run(sql, :array, params).to_a
25
+ end
26
+
23
27
  def exec(sql, *params)
24
28
  run(sql, :array, params)
25
29
  raw_connection.affected_rows
@@ -30,6 +34,11 @@ module MiniSql
30
34
  @deserializer_cache.materialize(result)
31
35
  end
32
36
 
37
+ def query_decorator(decorator, sql, *params)
38
+ result = run(sql, :array, params)
39
+ @deserializer_cache.materialize(result, decorator)
40
+ end
41
+
33
42
  def escape_string(str)
34
43
  raw_connection.escape(str)
35
44
  end
@@ -45,9 +54,9 @@ module MiniSql
45
54
  sql = param_encoder.encode(sql, *params)
46
55
  end
47
56
  raw_connection.query(
48
- sql,
49
- as: as,
50
- database_timezone: :utc,
57
+ sql,
58
+ as: as,
59
+ database_timezone: :utc,
51
60
  application_timezone: :utc,
52
61
  cast_booleans: true,
53
62
  cast: true,
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module MiniSql
2
4
  module Mysql
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
  key = result.fields
14
16
 
15
17
  # trivial fast LRU implementation
@@ -21,6 +23,10 @@ module MiniSql
21
23
  @cache.shift if @cache.length > @max_size
22
24
  end
23
25
 
26
+ if decorator_module
27
+ materializer = materializer.decorated(decorator_module)
28
+ end
29
+
24
30
  result.map do |data|
25
31
  materializer.materialize(data)
26
32
  end
@@ -32,23 +38,15 @@ module MiniSql
32
38
  fields = result.fields
33
39
 
34
40
  Class.new do
35
- attr_accessor(*fields)
41
+ extend MiniSql::Decoratable
42
+ include MiniSql::Result
36
43
 
37
- # AM serializer support
38
- alias :read_attribute_for_serialization :send
39
-
40
- def to_h
41
- r = {}
42
- instance_variables.each do |f|
43
- r[f.to_s.sub('@','').to_sym] = instance_variable_get(f)
44
- end
45
- r
46
- end
44
+ attr_accessor(*fields)
47
45
 
48
46
  instance_eval <<~RUBY
49
47
  def materialize(data)
50
48
  r = self.new
51
- #{col=-1; fields.map{|f| "r.#{f} = data[#{col+=1}]"}.join("; ")}
49
+ #{col = -1; fields.map { |f| "r.#{f} = data[#{col += 1}]" }.join("; ")}
52
50
  r
53
51
  end
54
52
  RUBY
@@ -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,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,9 @@ 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
+ if params && params.length > 0
93
+ sql = param_encoder.encode(sql, *params)
94
+ end
88
95
  conn = raw_connection
89
96
  conn.typemap = self.class.typemap
90
97
  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