mini_sql 1.3.0 → 1.5.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.
- checksums.yaml +4 -4
- data/CHANGELOG +10 -0
- data/README.md +29 -18
- data/lib/mini_sql/builder.rb +43 -26
- data/lib/mini_sql/connection.rb +8 -0
- data/lib/mini_sql/mysql/connection.rb +1 -8
- data/lib/mini_sql/postgres/connection.rb +1 -8
- data/lib/mini_sql/postgres/prepared_binds.rb +27 -0
- data/lib/mini_sql/postgres_jdbc/connection.rb +1 -8
- data/lib/mini_sql/sqlite/connection.rb +8 -10
- data/lib/mini_sql/sqlite/prepared_connection.rb +1 -1
- data/lib/mini_sql/version.rb +1 -1
- data/mini_sql.gemspec +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 491de0ce7925ad9ec428409af4a78e5ce3f91b6f886bfcca7c17544a4b44e23a
|
4
|
+
data.tar.gz: 2269da4aa96ec6d58c2141b82cae6d3764664e312ada254fe7f45476e6cb5b55
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dddf4370cdff6041fc2a0ceb338aa387ddcca76b65e135838cd0c3a1bfcf0bd0fcae37ea7504052b89b505b8c5478fb6d11e260e2349e25131b01853d42eae2d
|
7
|
+
data.tar.gz: 8d2f7087f39d05a4e8ccf72e675a07a38d9f371bdcfef33d96589a085f13abef53feb6986668fb12c75886ff9db301c0b7753b36e72177ef33944f08ce347734
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
2023-08-16 - 1.5.0
|
2
|
+
|
3
|
+
- FEATURE: add to_sql for easy conversion of builder to sql
|
4
|
+
- FEATURE: improve active record compat
|
5
|
+
- FEATURE: change builder to use gsub vs sub, which allows repeat clauses
|
6
|
+
|
7
|
+
2022-03-07 - 1.4.0
|
8
|
+
|
9
|
+
- PERF: Optimize multiple use param in prepared postgres sql
|
10
|
+
|
1
11
|
2022-02-02 - 1.3.0
|
2
12
|
|
3
13
|
- FEATURE: Add ActiveRecordPostgres connection
|
data/README.md
CHANGED
@@ -77,23 +77,34 @@ builder.query.each do |t|
|
|
77
77
|
end
|
78
78
|
```
|
79
79
|
|
80
|
+
The same builder's statement may occur multiple times.
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
builder = conn.build('(/*select*/ from books) union (/*select*/ from movies)').select('title').query
|
84
|
+
|
85
|
+
# => (SELECT title from books) union (SELECT title from movies)
|
86
|
+
```
|
87
|
+
|
80
88
|
The builder predefined next _SQL Literals_
|
81
89
|
|
82
|
-
| Method
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
90
|
+
| Method | SQL Literal |
|
91
|
+
|-------------|--------------|
|
92
|
+
| `select` | `/*select*/` |
|
93
|
+
| `count` | `/*select*/` |
|
94
|
+
| `where` | `/*where*/` |
|
95
|
+
| `where_or` | `/*where*/` |
|
96
|
+
| `where2` | `/*where2*/` |
|
97
|
+
| `where2_or` | `/*where2*/` |
|
98
|
+
| `join` | `/*join*/` |
|
99
|
+
| `left_join` | `/*left_join*/` |
|
100
|
+
| `group_by` | `/*group_by*/` |
|
101
|
+
| `order_by` | `/*order_by*/` |
|
102
|
+
| `limit` | `/*limit*/` |
|
103
|
+
| `offset` | `/*offset*/` |
|
104
|
+
| `set` | `/*set*/` |
|
94
105
|
|
95
106
|
### Custom SQL Literals
|
96
|
-
Use `sql_literal`
|
107
|
+
Use `sql_literal` to inject SQL into Builder from `String`, `Builder`, `ActiveRecord::Relation`, or any object that implements `to_sql` method.
|
97
108
|
|
98
109
|
```ruby
|
99
110
|
user_builder = conn
|
@@ -101,9 +112,9 @@ user_builder = conn
|
|
101
112
|
.where('type = ?', input_type)
|
102
113
|
.group_by("date_trunc('day', created_at)")
|
103
114
|
|
104
|
-
|
105
|
-
.
|
106
|
-
.where(
|
115
|
+
guest_relation = GuestTopic
|
116
|
+
.select("date_trunc('day', created_at) day, count(*)")
|
117
|
+
.where(state: input_state)
|
107
118
|
.group_by("date_trunc('day', created_at)")
|
108
119
|
|
109
120
|
conn
|
@@ -113,8 +124,8 @@ conn
|
|
113
124
|
from u
|
114
125
|
/*custom_join*/
|
115
126
|
SQL
|
116
|
-
.sql_literal(user: user_builder, guest:
|
117
|
-
.sql_literal(custom_join: "#{input_cond ? 'FULL' : 'LEFT'} JOIN g on g.day = u.day") # or
|
127
|
+
.sql_literal(user: user_builder, guest: guest_relation) # Builder and ActiveRecord::Relation
|
128
|
+
.sql_literal(custom_join: "#{input_cond ? 'FULL' : 'LEFT'} JOIN g on g.day = u.day") # or String
|
118
129
|
.query
|
119
130
|
```
|
120
131
|
|
data/lib/mini_sql/builder.rb
CHANGED
@@ -11,8 +11,13 @@ class MiniSql::Builder
|
|
11
11
|
@is_prepared = false
|
12
12
|
end
|
13
13
|
|
14
|
+
def initialize_copy(_original_builder)
|
15
|
+
@args = @args.transform_values { |v| v.dup }
|
16
|
+
@sections = @sections.transform_values { |v| v.dup }
|
17
|
+
end
|
18
|
+
|
14
19
|
literals1 =
|
15
|
-
[:set, :where2, :where, :order_by, :left_join, :join, :select, :group_by].each do |k|
|
20
|
+
[:set, :where2, :where2_or, :where, :where_or, :order_by, :left_join, :join, :select, :group_by].each do |k|
|
16
21
|
define_method k do |sql_part, *args|
|
17
22
|
if Hash === args[0]
|
18
23
|
@args.merge!(args[0])
|
@@ -48,7 +53,7 @@ class MiniSql::Builder
|
|
48
53
|
if PREDEFINED_SQL_LITERALS.include?(name)
|
49
54
|
raise "/*#{name}*/ is predefined, use method `.#{name}` instead `sql_literal`"
|
50
55
|
end
|
51
|
-
@sections[name] = part_sql.
|
56
|
+
@sections[name] = part_sql.respond_to?(:to_sql) ? part_sql.to_sql : part_sql
|
52
57
|
end
|
53
58
|
self
|
54
59
|
end
|
@@ -75,6 +80,10 @@ class MiniSql::Builder
|
|
75
80
|
@connection.param_encoder.encode(parametrized_sql, union_parameters(hash_args))
|
76
81
|
end
|
77
82
|
|
83
|
+
def count(field = '*')
|
84
|
+
dup.select("count(#{field})").query_single.first
|
85
|
+
end
|
86
|
+
|
78
87
|
private def connection_switcher
|
79
88
|
if @is_prepared
|
80
89
|
@connection.prepared
|
@@ -83,35 +92,43 @@ class MiniSql::Builder
|
|
83
92
|
end
|
84
93
|
end
|
85
94
|
|
95
|
+
WHERE_SECTIONS = [%i[where where_or], %i[where2 where2_or]]
|
86
96
|
private def parametrized_sql
|
87
97
|
sql = @sql.dup
|
88
98
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
joined = (+"SELECT ") << v.join(" , ")
|
94
|
-
when :where, :where2
|
95
|
-
joined = (+"WHERE ") << v.map { |c| (+"(") << c << ")" }.join(" AND ")
|
96
|
-
when :join
|
97
|
-
joined = v.map { |item| (+"JOIN ") << item }.join("\n")
|
98
|
-
when :left_join
|
99
|
-
joined = v.map { |item| (+"LEFT JOIN ") << item }.join("\n")
|
100
|
-
when :limit
|
101
|
-
joined = (+"LIMIT :mq_auto_limit")
|
102
|
-
when :offset
|
103
|
-
joined = (+"OFFSET :mq_auto_offset")
|
104
|
-
when :order_by
|
105
|
-
joined = (+"ORDER BY ") << v.join(" , ")
|
106
|
-
when :group_by
|
107
|
-
joined = (+"GROUP BY ") << v.join(" , ")
|
108
|
-
when :set
|
109
|
-
joined = (+"SET ") << v.join(" , ")
|
110
|
-
else # for sql_literal
|
111
|
-
joined = v
|
99
|
+
WHERE_SECTIONS.each do |section_and, section_or|
|
100
|
+
if (or_values = @sections.delete(section_or))
|
101
|
+
@sections[section_and] ||= []
|
102
|
+
@sections[section_and] << or_values.map { |c| "(#{c})" }.join(" OR ")
|
112
103
|
end
|
104
|
+
end
|
105
|
+
|
106
|
+
@sections.each do |k, v|
|
107
|
+
joined =
|
108
|
+
case k
|
109
|
+
when :select
|
110
|
+
"SELECT #{v.join(" , ")}"
|
111
|
+
when :where, :where2
|
112
|
+
"WHERE #{v.map { |c| "(#{c})" }.join(" AND ")}"
|
113
|
+
when :join
|
114
|
+
v.map { |item| "JOIN #{item}" }.join("\n")
|
115
|
+
when :left_join
|
116
|
+
v.map { |item| "LEFT JOIN #{item}" }.join("\n")
|
117
|
+
when :limit
|
118
|
+
"LIMIT :mq_auto_limit"
|
119
|
+
when :offset
|
120
|
+
"OFFSET :mq_auto_offset"
|
121
|
+
when :order_by
|
122
|
+
"ORDER BY #{v.join(" , ")}"
|
123
|
+
when :group_by
|
124
|
+
"GROUP BY #{v.join(" , ")}"
|
125
|
+
when :set
|
126
|
+
"SET #{v.join(" , ")}"
|
127
|
+
else # for sql_literal
|
128
|
+
v
|
129
|
+
end
|
113
130
|
|
114
|
-
unless sql.
|
131
|
+
unless sql.gsub!("/*#{k}*/", joined)
|
115
132
|
raise "The section for the /*#{k}*/ clause was not found!"
|
116
133
|
end
|
117
134
|
end
|
data/lib/mini_sql/connection.rb
CHANGED
@@ -57,6 +57,14 @@ module MiniSql
|
|
57
57
|
Builder.new(self, sql)
|
58
58
|
end
|
59
59
|
|
60
|
+
def to_sql(sql, *params)
|
61
|
+
if params.empty?
|
62
|
+
sql
|
63
|
+
else
|
64
|
+
param_encoder.encode(sql, *params)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
60
68
|
def escape_string(str)
|
61
69
|
raise NotImplementedError, "must be implemented by child connection"
|
62
70
|
end
|
@@ -51,18 +51,11 @@ module MiniSql
|
|
51
51
|
raw_connection.escape(str)
|
52
52
|
end
|
53
53
|
|
54
|
-
def build(sql)
|
55
|
-
Builder.new(self, sql)
|
56
|
-
end
|
57
|
-
|
58
54
|
private
|
59
55
|
|
60
56
|
def run(sql, as, params)
|
61
|
-
if params && params.length > 0
|
62
|
-
sql = param_encoder.encode(sql, *params)
|
63
|
-
end
|
64
57
|
raw_connection.query(
|
65
|
-
sql,
|
58
|
+
to_sql(sql, *params),
|
66
59
|
as: as,
|
67
60
|
database_timezone: :utc,
|
68
61
|
application_timezone: :utc,
|
@@ -192,10 +192,6 @@ module MiniSql
|
|
192
192
|
result.clear if result
|
193
193
|
end
|
194
194
|
|
195
|
-
def build(sql)
|
196
|
-
Builder.new(self, sql)
|
197
|
-
end
|
198
|
-
|
199
195
|
def escape_string(str)
|
200
196
|
raw_connection.escape_string(str)
|
201
197
|
end
|
@@ -203,10 +199,7 @@ module MiniSql
|
|
203
199
|
private
|
204
200
|
|
205
201
|
def run(sql, params)
|
206
|
-
|
207
|
-
sql = param_encoder.encode(sql, *params)
|
208
|
-
end
|
209
|
-
raw_connection.async_exec(sql)
|
202
|
+
raw_connection.async_exec(to_sql(sql, *params))
|
210
203
|
end
|
211
204
|
|
212
205
|
end
|
@@ -6,6 +6,33 @@ module MiniSql
|
|
6
6
|
module Postgres
|
7
7
|
class PreparedBinds < ::MiniSql::Abstract::PreparedBinds
|
8
8
|
|
9
|
+
def bind_hash(sql, hash)
|
10
|
+
sql = sql.dup
|
11
|
+
binds = []
|
12
|
+
bind_names = []
|
13
|
+
i = 0
|
14
|
+
|
15
|
+
hash.each do |k, v|
|
16
|
+
bind_outputs =
|
17
|
+
array_wrap(v).map { |vv|
|
18
|
+
binds << vv
|
19
|
+
bind_names << [BindName.new(k)]
|
20
|
+
bind_output(i += 1)
|
21
|
+
}.join(', ')
|
22
|
+
|
23
|
+
sql.gsub!(":#{k}") do
|
24
|
+
# ignore ::int and stuff like that
|
25
|
+
# $` is previous to match
|
26
|
+
if $` && $`[-1] != ":"
|
27
|
+
bind_outputs
|
28
|
+
else
|
29
|
+
":#{k}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
[sql, binds, bind_names]
|
34
|
+
end
|
35
|
+
|
9
36
|
def bind_output(i)
|
10
37
|
"$#{i}"
|
11
38
|
end
|
@@ -78,10 +78,6 @@ module MiniSql
|
|
78
78
|
run(sql, params).to_a
|
79
79
|
end
|
80
80
|
|
81
|
-
def build(sql)
|
82
|
-
Builder.new(self, sql)
|
83
|
-
end
|
84
|
-
|
85
81
|
def escape_string(str)
|
86
82
|
raw_connection.escape_string(str)
|
87
83
|
end
|
@@ -89,12 +85,9 @@ module MiniSql
|
|
89
85
|
private
|
90
86
|
|
91
87
|
def run(sql, params)
|
92
|
-
if params && params.length > 0
|
93
|
-
sql = param_encoder.encode(sql, *params)
|
94
|
-
end
|
95
88
|
conn = raw_connection
|
96
89
|
conn.typemap = self.class.typemap
|
97
|
-
conn.execute(sql)
|
90
|
+
conn.execute(to_sql(sql, *params))
|
98
91
|
ensure
|
99
92
|
# Force unsetting of typemap since we don't want mixed AR usage to continue to use these extra converters.
|
100
93
|
conn.typemap = nil
|
@@ -21,12 +21,12 @@ module MiniSql
|
|
21
21
|
|
22
22
|
def query_single(sql, *params)
|
23
23
|
# a bit lazy can be optimized
|
24
|
-
run(sql,
|
24
|
+
run(sql, params).flatten!
|
25
25
|
end
|
26
26
|
|
27
27
|
def query_hash(sql, *params)
|
28
28
|
r = []
|
29
|
-
run(sql,
|
29
|
+
run(sql, params) do |set|
|
30
30
|
set.each_hash do |h|
|
31
31
|
r << h
|
32
32
|
end
|
@@ -35,14 +35,14 @@ module MiniSql
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def query_array(sql, *params)
|
38
|
-
run(sql,
|
38
|
+
run(sql, params)
|
39
39
|
end
|
40
40
|
|
41
41
|
def exec(sql, *params)
|
42
42
|
|
43
43
|
start = raw_connection.total_changes
|
44
44
|
|
45
|
-
r = run(sql,
|
45
|
+
r = run(sql, params)
|
46
46
|
# this is not safe for multithreading, also for DELETE from TABLE will return
|
47
47
|
# incorrect data
|
48
48
|
if r.length > 0
|
@@ -53,13 +53,13 @@ module MiniSql
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def query(sql, *params)
|
56
|
-
run(sql,
|
56
|
+
run(sql, params) do |set|
|
57
57
|
deserializer_cache.materialize(set)
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
61
|
def query_decorator(decorator, sql, *params)
|
62
|
-
run(sql,
|
62
|
+
run(sql, params) do |set|
|
63
63
|
deserializer_cache.materialize(set, decorator)
|
64
64
|
end
|
65
65
|
end
|
@@ -70,10 +70,8 @@ module MiniSql
|
|
70
70
|
|
71
71
|
private
|
72
72
|
|
73
|
-
def run(sql,
|
74
|
-
|
75
|
-
sql = param_encoder.encode(sql, *params)
|
76
|
-
end
|
73
|
+
def run(sql, params)
|
74
|
+
sql = to_sql(sql, *params)
|
77
75
|
if block_given?
|
78
76
|
stmt = SQLite3::Statement.new(raw_connection, sql)
|
79
77
|
result = yield stmt.execute
|
@@ -28,7 +28,7 @@ module MiniSql
|
|
28
28
|
end
|
29
29
|
|
30
30
|
private def run(sql, params)
|
31
|
-
prepared_sql, binds, _bind_names = @param_binder.bind(sql, params)
|
31
|
+
prepared_sql, binds, _bind_names = @param_binder.bind(sql, *params)
|
32
32
|
statement = @prepared_cache.prepare_statement(prepared_sql)
|
33
33
|
statement.bind_params(binds)
|
34
34
|
if block_given?
|
data/lib/mini_sql/version.rb
CHANGED
data/mini_sql.gemspec
CHANGED
@@ -47,7 +47,7 @@ Gem::Specification.new do |spec|
|
|
47
47
|
else
|
48
48
|
spec.add_development_dependency "pg", "> 1"
|
49
49
|
spec.add_development_dependency "mysql2"
|
50
|
-
spec.add_development_dependency "sqlite3", "~> 1.
|
50
|
+
spec.add_development_dependency "sqlite3", "~> 1.4.4"
|
51
51
|
spec.add_development_dependency "activerecord", "~> 7.0.0"
|
52
52
|
end
|
53
53
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mini_sql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Saffron
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-08-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -170,14 +170,14 @@ dependencies:
|
|
170
170
|
requirements:
|
171
171
|
- - "~>"
|
172
172
|
- !ruby/object:Gem::Version
|
173
|
-
version:
|
173
|
+
version: 1.4.4
|
174
174
|
type: :development
|
175
175
|
prerelease: false
|
176
176
|
version_requirements: !ruby/object:Gem::Requirement
|
177
177
|
requirements:
|
178
178
|
- - "~>"
|
179
179
|
- !ruby/object:Gem::Version
|
180
|
-
version:
|
180
|
+
version: 1.4.4
|
181
181
|
- !ruby/object:Gem::Dependency
|
182
182
|
name: activerecord
|
183
183
|
requirement: !ruby/object:Gem::Requirement
|