mini_sql 0.2.3 → 1.0.1
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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +66 -0
- data/.rubocop-https---raw-githubusercontent-com-discourse-discourse-master--rubocop-yml +355 -0
- data/.rubocop.yml +8 -0
- data/CHANGELOG +22 -0
- data/Gemfile +3 -1
- data/Guardfile +2 -0
- data/README.md +89 -1
- data/Rakefile +3 -1
- data/bench/timestamp_perf.rb +22 -21
- data/bench/topic_mysql_perf.rb +1 -7
- data/bench/topic_perf.rb +174 -10
- data/bin/console +1 -0
- data/lib/mini_sql.rb +3 -0
- data/lib/mini_sql/builder.rb +10 -1
- data/lib/mini_sql/connection.rb +15 -3
- data/lib/mini_sql/decoratable.rb +22 -0
- data/lib/mini_sql/deserializer_cache.rb +2 -0
- data/lib/mini_sql/inline_param_encoder.rb +9 -11
- data/lib/mini_sql/mysql/connection.rb +12 -3
- data/lib/mini_sql/mysql/deserializer_cache.rb +11 -13
- data/lib/mini_sql/postgres/coders.rb +2 -0
- data/lib/mini_sql/postgres/connection.rb +83 -0
- data/lib/mini_sql/postgres/deserializer_cache.rb +33 -13
- data/lib/mini_sql/postgres_jdbc/connection.rb +8 -1
- data/lib/mini_sql/postgres_jdbc/deserializer_cache.rb +43 -43
- data/lib/mini_sql/result.rb +20 -0
- data/lib/mini_sql/serializer.rb +70 -0
- data/lib/mini_sql/sqlite/connection.rb +11 -1
- data/lib/mini_sql/sqlite/deserializer_cache.rb +11 -13
- data/lib/mini_sql/version.rb +1 -1
- data/mini_sql.gemspec +14 -3
- metadata +63 -13
- data/.travis.yml +0 -26
data/bin/console
CHANGED
data/lib/mini_sql.rb
CHANGED
@@ -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'
|
data/lib/mini_sql/builder.rb
CHANGED
@@ -66,5 +66,14 @@ class MiniSql::Builder
|
|
66
66
|
RUBY
|
67
67
|
end
|
68
68
|
|
69
|
-
|
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
|
data/lib/mini_sql/connection.rb
CHANGED
@@ -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
|
34
|
+
def query_hash(sql, *params)
|
35
35
|
raise NotImplementedError, "must be implemented by child connection"
|
36
36
|
end
|
37
37
|
|
38
|
-
def
|
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
|
@@ -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
|
-
|
41
|
+
extend MiniSql::Decoratable
|
42
|
+
include MiniSql::Result
|
36
43
|
|
37
|
-
|
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
|
49
|
+
#{col = -1; fields.map { |f| "r.#{f} = data[#{col += 1}]" }.join("; ")}
|
52
50
|
r
|
53
51
|
end
|
54
52
|
RUBY
|
@@ -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
|
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
|
-
|
43
|
-
|
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
|
-
|
46
|
-
|
70
|
+
Class.new do
|
71
|
+
extend MiniSql::Decoratable
|
72
|
+
include MiniSql::Result
|
47
73
|
|
48
|
-
|
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
|
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
|
-
|
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
|
-
|
7
|
+
DEFAULT_MAX_SIZE = 500
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
def initialize(max_size = nil)
|
10
|
+
@cache = {}
|
11
|
+
@max_size = max_size || DEFAULT_MAX_SIZE
|
12
|
+
end
|
11
13
|
|
12
|
-
|
14
|
+
def materialize(result, decorator_module = nil)
|
13
15
|
|
14
|
-
|
16
|
+
return [] if result.ntuples == 0
|
15
17
|
|
16
|
-
|
18
|
+
key = result.fields
|
17
19
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
40
|
-
fields = result.fields
|
45
|
+
private
|
41
46
|
|
42
|
-
|
43
|
-
|
47
|
+
def new_row_matrializer(result)
|
48
|
+
fields = result.fields
|
44
49
|
|
45
|
-
|
46
|
-
|
50
|
+
Class.new do
|
51
|
+
extend MiniSql::Decoratable
|
52
|
+
include MiniSql::Result
|
47
53
|
|
48
|
-
|
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
|
-
|
56
|
+
instance_eval <<~RUBY
|
57
57
|
def materialize(pg_result, index)
|
58
58
|
r = self.new
|
59
|
-
#{col
|
59
|
+
#{col = -1; fields.map { |f| "r.#{f} = pg_result.getvalue(index, #{col += 1})" }.join("; ")}
|
60
60
|
r
|
61
61
|
end
|
62
|
-
|
62
|
+
RUBY
|
63
|
+
end
|
63
64
|
end
|
64
65
|
end
|
65
66
|
end
|
66
67
|
end
|
67
|
-
end
|