sql_helper 0.1.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.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +40 -0
- data/Rakefile +7 -0
- data/lib/sql_helper/version.rb +3 -0
- data/lib/sql_helper.rb +348 -0
- data/sql_helper.gemspec +24 -0
- data/test/test_sql_helper.rb +179 -0
- metadata +110 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Junegunn Choi
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# SQLHelper
|
2
|
+
|
3
|
+
A simplistic SQL generator extracted from [jdbc-helper](https://github.com/junegunn/jdbc-helper) gem.
|
4
|
+
This gem is not intended to be used independently outside the context of jdbc-helper.
|
5
|
+
|
6
|
+
## Example
|
7
|
+
|
8
|
+
```ruby
|
9
|
+
SQLHelper.select(
|
10
|
+
prepared: true,
|
11
|
+
table: 'mytable',
|
12
|
+
project: %w[a b c d e],
|
13
|
+
where: [
|
14
|
+
'z <> 100',
|
15
|
+
['y = ?', 200],
|
16
|
+
{
|
17
|
+
a: "hello 'world'",
|
18
|
+
b: (1..10),
|
19
|
+
c: (1...10),
|
20
|
+
d: ['abc', "'def'"],
|
21
|
+
e: { sql: 'sysdate' },
|
22
|
+
f: { not: nil },
|
23
|
+
g: { gt: 100 },
|
24
|
+
h: { lt: 100 },
|
25
|
+
i: { like: 'ABC%' },
|
26
|
+
j: { not: { like: 'ABC%' } },
|
27
|
+
k: { le: { sql: 'sysdate' } },
|
28
|
+
l: { ge: 100, le: 200 },
|
29
|
+
m: { not: [ 150, { ge: 100, le: 200 } ] },
|
30
|
+
n: nil,
|
31
|
+
o: { not: (1..10) },
|
32
|
+
p: { or: [{ gt: 100 }, { lt: 50 }] },
|
33
|
+
q: { like: ['ABC%', 'DEF%'] },
|
34
|
+
r: { or: [{ like: ['ABC%', 'DEF%'] }, { not: { like: 'XYZ%' } }] }
|
35
|
+
}
|
36
|
+
],
|
37
|
+
order: 'a desc',
|
38
|
+
limit: 10
|
39
|
+
)
|
40
|
+
```
|
data/Rakefile
ADDED
data/lib/sql_helper.rb
ADDED
@@ -0,0 +1,348 @@
|
|
1
|
+
require "sql_helper/version"
|
2
|
+
require 'set'
|
3
|
+
require 'bigdecimal'
|
4
|
+
|
5
|
+
module SQLHelper
|
6
|
+
class << self
|
7
|
+
def escape arg
|
8
|
+
arg.to_s.gsub("'", "''")
|
9
|
+
end
|
10
|
+
|
11
|
+
# A naive check
|
12
|
+
def check expr
|
13
|
+
expr = expr.to_s
|
14
|
+
test = expr.to_s.gsub(/(['`"]).*?\1/, '').
|
15
|
+
gsub(%r{/\*.*?\*/}, '').
|
16
|
+
strip
|
17
|
+
raise SyntaxError.new("cannot contain unquoted semi-colons: #{expr}") if test.include?(';')
|
18
|
+
raise SyntaxError.new("cannot contain unquoted comments: #{expr}") if test.match(%r{--|/\*|\*/})
|
19
|
+
raise SyntaxError.new("unclosed quotation mark: #{expr}") if test.match(/['"`]/)
|
20
|
+
raise SyntaxError.new("empty expression") if expr.strip.empty?
|
21
|
+
expr
|
22
|
+
end
|
23
|
+
|
24
|
+
def quote arg
|
25
|
+
case arg
|
26
|
+
when String, Symbol
|
27
|
+
"'#{arg.to_s.gsub "'", "''"}'"
|
28
|
+
when BigDecimal
|
29
|
+
arg.to_s('F')
|
30
|
+
when nil
|
31
|
+
'null'
|
32
|
+
else
|
33
|
+
if expr?(arg)
|
34
|
+
arg.values.first
|
35
|
+
else
|
36
|
+
arg.to_s
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def insert arg
|
42
|
+
insert_internal 'insert into', arg
|
43
|
+
end
|
44
|
+
|
45
|
+
def insert_ignore arg
|
46
|
+
insert_internal 'insert ignore into', arg
|
47
|
+
end
|
48
|
+
|
49
|
+
def replace arg
|
50
|
+
insert_internal 'replace into', arg
|
51
|
+
end
|
52
|
+
|
53
|
+
def delete args
|
54
|
+
check_keys args, Set[:table, :where, :prepared]
|
55
|
+
|
56
|
+
wc, *wp = where_internal args[:prepared], args[:where]
|
57
|
+
sql = "delete from #{args.fetch :table} #{wc}".strip
|
58
|
+
if args[:prepared]
|
59
|
+
[sql, *wp]
|
60
|
+
else
|
61
|
+
sql
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def update args
|
66
|
+
check_keys args, Set[:prepared, :where, :table, :data]
|
67
|
+
table = args.fetch(:table)
|
68
|
+
data = args.fetch(:data)
|
69
|
+
prepared = args[:prepared]
|
70
|
+
where,
|
71
|
+
*wbind = where_internal(args[:prepared], args[:where])
|
72
|
+
bind = []
|
73
|
+
vals = data.map { |k, v|
|
74
|
+
if prepared
|
75
|
+
if expr?(v)
|
76
|
+
[k, v.values.first].join(' = ')
|
77
|
+
else
|
78
|
+
bind << v
|
79
|
+
"#{k} = ?"
|
80
|
+
end
|
81
|
+
else
|
82
|
+
[k, quote(v)].join(' = ')
|
83
|
+
end
|
84
|
+
}
|
85
|
+
sql = "update #{check table} set #{vals.join ', '} #{where}".strip
|
86
|
+
if prepared
|
87
|
+
[sql] + bind + wbind
|
88
|
+
else
|
89
|
+
sql
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def count args
|
94
|
+
check_keys args, Set[:prepared, :where, :table]
|
95
|
+
select args.merge(:project => 'count(*)')
|
96
|
+
end
|
97
|
+
|
98
|
+
def select args
|
99
|
+
check_keys args, Set[:prepared, :project, :where, :order, :limit, :top, :table]
|
100
|
+
|
101
|
+
top = args[:top] ? "top #{args[:top]}" : ''
|
102
|
+
project = project(*args[:project])
|
103
|
+
where,
|
104
|
+
*params = args[:where] ? where_internal(args[:prepared], args[:where]) : ['']
|
105
|
+
order = order(*args[:order])
|
106
|
+
limit = limit(*args[:limit])
|
107
|
+
|
108
|
+
sql = ['select', top, project, 'from', args.fetch(:table),
|
109
|
+
where, order, limit].reject(&:empty?).join(' ')
|
110
|
+
if args[:prepared]
|
111
|
+
[ sql, *params ]
|
112
|
+
else
|
113
|
+
sql
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def project *args
|
118
|
+
args = args.compact.map(&:to_s).map(&:strip).reject(&:empty?)
|
119
|
+
if args.empty?
|
120
|
+
'*'
|
121
|
+
else
|
122
|
+
check args.join ', '
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def order *args
|
127
|
+
args = args.compact.map(&:to_s).map(&:strip).reject(&:empty?)
|
128
|
+
if args.empty?
|
129
|
+
''
|
130
|
+
else
|
131
|
+
check "order by #{args.join ', '}"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def limit *args
|
136
|
+
limit, offset = args.reverse.map { |e|
|
137
|
+
s = e.to_s.strip
|
138
|
+
s.empty? ? nil : s
|
139
|
+
}
|
140
|
+
arg = arg.to_s.strip
|
141
|
+
if offset
|
142
|
+
check "limit #{offset}, #{limit}"
|
143
|
+
elsif limit
|
144
|
+
check "limit #{limit}"
|
145
|
+
else
|
146
|
+
''
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def where *conds
|
151
|
+
where_internal false, conds
|
152
|
+
end
|
153
|
+
|
154
|
+
def where_prepared *conds
|
155
|
+
where_internal true, conds
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
OPERATOR_MAP = {
|
160
|
+
:gt => '>',
|
161
|
+
:ge => '>=',
|
162
|
+
:lt => '<',
|
163
|
+
:le => '<=',
|
164
|
+
:ne => '<>',
|
165
|
+
:> => '>',
|
166
|
+
:>= => '>=',
|
167
|
+
:< => '>',
|
168
|
+
:<= => '>=',
|
169
|
+
:like => 'like'
|
170
|
+
}
|
171
|
+
|
172
|
+
def expr? val
|
173
|
+
val.is_a?(Hash) && val.length == 1 && [:sql, :expr].include?(val.keys.first)
|
174
|
+
end
|
175
|
+
|
176
|
+
def check_keys args, known
|
177
|
+
raise ArgumentError, "hash expected" unless args.is_a?(Hash)
|
178
|
+
unknown = Set.new(args.keys) - known
|
179
|
+
raise ArgumentError, "unknown keys: #{unknown.to_a.join ', '}" unless unknown.empty?
|
180
|
+
end
|
181
|
+
|
182
|
+
def where_internal prepared, conds
|
183
|
+
sqls = []
|
184
|
+
params = []
|
185
|
+
|
186
|
+
conds =
|
187
|
+
case conds
|
188
|
+
when String, Hash
|
189
|
+
[conds]
|
190
|
+
when nil
|
191
|
+
[]
|
192
|
+
when Array
|
193
|
+
conds
|
194
|
+
else
|
195
|
+
raise ArgumentError, "invalid argument: #{conds.class}"
|
196
|
+
end
|
197
|
+
|
198
|
+
conds.each do |cond|
|
199
|
+
case cond
|
200
|
+
when String
|
201
|
+
sql = cond.strip
|
202
|
+
next if sql.empty?
|
203
|
+
sqls << "(#{check sql})"
|
204
|
+
when Array
|
205
|
+
sql = cond[0].to_s.strip
|
206
|
+
next if sql.empty?
|
207
|
+
sql = check sql
|
208
|
+
if prepared
|
209
|
+
sqls << "(#{sql})"
|
210
|
+
params += cond[1..-1]
|
211
|
+
else
|
212
|
+
params = cond[1..-1]
|
213
|
+
sql = "(#{sql})".gsub('?') {
|
214
|
+
if params.empty?
|
215
|
+
'?'
|
216
|
+
else
|
217
|
+
quote params.shift
|
218
|
+
end
|
219
|
+
}
|
220
|
+
sqls << sql
|
221
|
+
end
|
222
|
+
when Hash
|
223
|
+
cond.each do |col, cnd|
|
224
|
+
ret = eval_hash_cond col, cnd, prepared
|
225
|
+
sqls << ret[0]
|
226
|
+
params += ret[1..-1] || []
|
227
|
+
end
|
228
|
+
when nil
|
229
|
+
else
|
230
|
+
raise ArgumentError, "invalid condition: #{cond}"
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
if prepared
|
235
|
+
sqls.empty? ? [''] : ["where #{sqls.join ' and '}"].concat(params)
|
236
|
+
else
|
237
|
+
sqls.empty? ? '' : "where #{sqls.join ' and '}"
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def eval_hash_cond col, cnd, prepared
|
242
|
+
case cnd
|
243
|
+
when Numeric, String
|
244
|
+
prepared ? ["#{col} = ?", cnd] : [[col, quote(cnd)].join(' = ')]
|
245
|
+
when Range
|
246
|
+
if cnd.exclude_end?
|
247
|
+
prepared ?
|
248
|
+
["#{col} >= ? and #{col} < ?", cnd.begin, cnd.end] :
|
249
|
+
["#{col} >= #{quote cnd.begin} and #{col} < #{quote cnd.end}"]
|
250
|
+
else
|
251
|
+
prepared ?
|
252
|
+
["#{col} between ? and ?", cnd.begin, cnd.end] :
|
253
|
+
["#{col} between #{quote cnd.begin} and #{quote cnd.end}"]
|
254
|
+
end
|
255
|
+
when Array
|
256
|
+
sqls = []
|
257
|
+
params = []
|
258
|
+
cnd.each do |v|
|
259
|
+
ret = eval_hash_cond col, v, prepared
|
260
|
+
sqls << ret[0]
|
261
|
+
params += ret[1..-1]
|
262
|
+
end
|
263
|
+
["(#{sqls.join(' or ')})", *params]
|
264
|
+
when nil
|
265
|
+
["#{col} is null"]
|
266
|
+
when Hash
|
267
|
+
rets = cnd.map { |op, val|
|
268
|
+
case op
|
269
|
+
when :expr, :sql
|
270
|
+
["#{col} = #{check val}"]
|
271
|
+
when :not
|
272
|
+
case val
|
273
|
+
when nil
|
274
|
+
["#{col} is not null"]
|
275
|
+
when String, Numeric
|
276
|
+
prepared ?
|
277
|
+
["#{col} <> ?", val] :
|
278
|
+
["#{col} <> #{quote val}"]
|
279
|
+
else
|
280
|
+
ary = eval_hash_cond col, val, prepared
|
281
|
+
ary[0].prepend 'not '
|
282
|
+
ary
|
283
|
+
end
|
284
|
+
when :or
|
285
|
+
sqls = []
|
286
|
+
params = []
|
287
|
+
val.each do |v|
|
288
|
+
ret = eval_hash_cond col, v, prepared
|
289
|
+
sqls << ret[0]
|
290
|
+
params += ret[1..-1]
|
291
|
+
end
|
292
|
+
["(#{sqls.join(' or ')})", *params]
|
293
|
+
when :gt, :>, :ge, :>=, :lt, :<, :le, :<=, :ne, :like
|
294
|
+
if val.is_a?(Hash)
|
295
|
+
if expr?(val)
|
296
|
+
[[col, OPERATOR_MAP[op], check(val.values.first)].join(' ')]
|
297
|
+
else
|
298
|
+
raise ArgumentError, "invalid condition"
|
299
|
+
end
|
300
|
+
elsif val.is_a?(Array)
|
301
|
+
prepared ?
|
302
|
+
["(#{(["#{col} #{OPERATOR_MAP[op]} ?"] * val.length).join(' or ')})", *val] :
|
303
|
+
["(#{val.map { |v| [col, OPERATOR_MAP[op], quote(v)].join ' ' }.join(' or ')})"]
|
304
|
+
else
|
305
|
+
prepared ?
|
306
|
+
["#{col} #{OPERATOR_MAP[op]} ?", val] :
|
307
|
+
[[col, OPERATOR_MAP[op], quote(val)].join(' ')]
|
308
|
+
end
|
309
|
+
else
|
310
|
+
raise ArgumentError, "unexpected operator: #{op}"
|
311
|
+
end
|
312
|
+
}
|
313
|
+
[rets.map(&:first).join(' and '), *rets.inject([]) { |prms, r| prms.concat r[1..-1] }]
|
314
|
+
else
|
315
|
+
raise ArgumentError, "invalid condition: #{cnd}"
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
def insert_internal prefix, args
|
320
|
+
check_keys args, Set[:table, :data, :prepared]
|
321
|
+
prep = args[:prepared]
|
322
|
+
into = args.fetch(:table)
|
323
|
+
data = args.fetch(:data)
|
324
|
+
|
325
|
+
bind = []
|
326
|
+
cols = data.keys
|
327
|
+
vals = data.values.map { |val|
|
328
|
+
if prep
|
329
|
+
if expr?(val)
|
330
|
+
val.values.first
|
331
|
+
else
|
332
|
+
bind << val
|
333
|
+
'?'
|
334
|
+
end
|
335
|
+
else
|
336
|
+
quote(val)
|
337
|
+
end
|
338
|
+
}
|
339
|
+
|
340
|
+
sql = "#{prefix} #{into} (#{cols.join ', '}) values (#{vals.join ', '})"
|
341
|
+
if args[:prepared]
|
342
|
+
[sql] + bind
|
343
|
+
else
|
344
|
+
sql
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|
348
|
+
end
|
data/sql_helper.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'sql_helper/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "sql_helper"
|
8
|
+
spec.version = SQLHelper::VERSION
|
9
|
+
spec.authors = ["Junegunn Choi"]
|
10
|
+
spec.email = ["junegunn.c@gmail.com"]
|
11
|
+
spec.description = %q{A simplistic SQL generator}
|
12
|
+
spec.summary = %q{A simplistic SQL generator}
|
13
|
+
spec.homepage = "https://github.com/junegunn/sql_helper"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "minitest"
|
24
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
$VERBOSE = true
|
2
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
3
|
+
require 'sql_helper'
|
4
|
+
require 'bigdecimal'
|
5
|
+
require 'minitest/autorun'
|
6
|
+
|
7
|
+
class TestSQLHelper < MiniTest::Unit::TestCase
|
8
|
+
def setup
|
9
|
+
@conds = [
|
10
|
+
'z <> 100',
|
11
|
+
["y = ? or y = ? or y = ? or z = '???'", 200, "Macy's???", BigDecimal('3.141592')],
|
12
|
+
{
|
13
|
+
:a => "hello 'world'???",
|
14
|
+
:b => (1..10),
|
15
|
+
:c => (1...10),
|
16
|
+
:d => ['abc', "'def'"],
|
17
|
+
:e => { :sql => 'sysdate' },
|
18
|
+
:f => { :not => nil },
|
19
|
+
:g => { :gt => 100 },
|
20
|
+
:h => { :lt => 100 },
|
21
|
+
:i => { :like => 'ABC%' },
|
22
|
+
:j => { :not => { :like => 'ABC%' } },
|
23
|
+
:k => { :le => { :sql => 'sysdate' } },
|
24
|
+
:l => { :ge => 100, :le => 200 },
|
25
|
+
:m => { :not => [ 150, { :ge => 100, :le => 200 } ] },
|
26
|
+
:n => nil,
|
27
|
+
:o => { :not => (1..10) },
|
28
|
+
:p => { :or => [{ :gt => 100 }, { :lt => 50 }] },
|
29
|
+
:q => { :like => ['ABC%', 'DEF%'] },
|
30
|
+
:r => { :or => [{ :like => ['ABC%', 'DEF%'] }, { :not => { :like => 'XYZ%' } }] },
|
31
|
+
:s => { :not => 100 },
|
32
|
+
:t => { :not => 'str' },
|
33
|
+
:u => ('aa'..'zz')
|
34
|
+
},
|
35
|
+
[],
|
36
|
+
[nil],
|
37
|
+
'',
|
38
|
+
[' '],
|
39
|
+
' '
|
40
|
+
]
|
41
|
+
|
42
|
+
@wherep = ["where (z <> 100) and (y = ? or y = ? or y = ? or z = 'xxx') and a = ? and b between ? and ? and c >= ? and c < ? and (d = ? or d = ?) and e = sysdate and f is not null and g > ? and h < ? and i like ? and not j like ? and k <= sysdate and l >= ? and l <= ? and not (m = ? or m >= ? and m <= ?) and n is null and not o between ? and ? and (p > ? or p < ?) and (q like ? or q like ?) and ((r like ? or r like ?) or not r like ?) and s <> ? and t <> ? and u between ? and ?",
|
43
|
+
200, "Macy's???", BigDecimal("3.141592"),
|
44
|
+
"hello 'world'???",
|
45
|
+
1, 10,
|
46
|
+
1, 10,
|
47
|
+
'abc', "'def'",
|
48
|
+
100,
|
49
|
+
100,
|
50
|
+
'ABC%',
|
51
|
+
'ABC%',
|
52
|
+
100, 200,
|
53
|
+
150, 100, 200,
|
54
|
+
1, 10,
|
55
|
+
100, 50,
|
56
|
+
'ABC%', 'DEF%',
|
57
|
+
'ABC%', 'DEF%', 'XYZ%',
|
58
|
+
100,
|
59
|
+
'str',
|
60
|
+
'aa', 'zz'
|
61
|
+
]
|
62
|
+
|
63
|
+
params = @wherep[1..-1]
|
64
|
+
@where = @wherep[0].gsub('?') {
|
65
|
+
if params.empty?
|
66
|
+
'?'
|
67
|
+
else
|
68
|
+
SQLHelper.quote params.shift
|
69
|
+
end
|
70
|
+
}.sub('xxx', '???')
|
71
|
+
@wherep[0].sub!('xxx', '???')
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_where
|
75
|
+
assert_equal @where, SQLHelper.where(*@conds)
|
76
|
+
assert_equal @where, SQLHelper.where(*(@conds + [nil, [], nil]))
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_where_prepared
|
80
|
+
assert_equal @wherep, SQLHelper.where_prepared(*@conds)
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_where_invalid
|
84
|
+
assert_raises(ArgumentError) { SQLHelper.where(5) }
|
85
|
+
assert_raises(ArgumentError) { SQLHelper.where(:true) }
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_select
|
89
|
+
sql, *params = SQLHelper.select(
|
90
|
+
:prepared => true,
|
91
|
+
:project => [:a, :b, :c],
|
92
|
+
:table => :mytable,
|
93
|
+
:top => 10,
|
94
|
+
:where => @conds,
|
95
|
+
:order => 'a desc',
|
96
|
+
:limit => 100
|
97
|
+
)
|
98
|
+
assert_equal "select top 10 a, b, c from mytable #{@wherep[0]} order by a desc limit 100", sql
|
99
|
+
assert_equal @wherep[1..-1], params
|
100
|
+
|
101
|
+
sql, *params = SQLHelper.select(
|
102
|
+
:prepared => true,
|
103
|
+
:project => '*',
|
104
|
+
:table => 'mytable',
|
105
|
+
:where => @conds,
|
106
|
+
:order => ['a desc', 'b asc'],
|
107
|
+
:limit => [100, 300]
|
108
|
+
)
|
109
|
+
assert_equal "select * from mytable #{@wherep[0]} order by a desc, b asc limit 100, 300", sql
|
110
|
+
assert_equal @wherep[1..-1], params
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_count
|
114
|
+
sql, *params = SQLHelper.count(
|
115
|
+
:prepared => true,
|
116
|
+
:table => :mytable,
|
117
|
+
:where => @conds
|
118
|
+
)
|
119
|
+
assert_equal "select count(*) from mytable #{@wherep[0]}", sql
|
120
|
+
assert_equal @wherep[1..-1], params
|
121
|
+
assert_equal "select count(*) from mytable where a is not null",
|
122
|
+
SQLHelper.count(:prepared => false, :table => :mytable, :where => { :a => { :not => nil } })
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_insert
|
126
|
+
[:insert, :insert_ignore, :replace].each do |m|
|
127
|
+
sql, *binds = SQLHelper.send(m,
|
128
|
+
:table => :mytable,
|
129
|
+
:data => { :a => 100, :b => 200, :c => { :sql => 'sysdate' }, :d => "hello 'world'" },
|
130
|
+
:prepared => true
|
131
|
+
)
|
132
|
+
assert_equal %[#{m.to_s.gsub '_', ' '} into mytable (a, b, c, d) values (?, ?, sysdate, ?)], sql
|
133
|
+
assert_equal [100, 200, "hello 'world'"], binds
|
134
|
+
|
135
|
+
sql = SQLHelper.send(m,
|
136
|
+
:table => :mytable,
|
137
|
+
:data => { :a => 100, :b => 200, :c => { :sql => 'sysdate' }, :d => "hello 'world'" },
|
138
|
+
:prepared => false
|
139
|
+
)
|
140
|
+
assert_equal %[#{m.to_s.gsub '_', ' '} into mytable (a, b, c, d) values (100, 200, sysdate, 'hello ''world''')], sql
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def test_delete
|
145
|
+
del = SQLHelper.delete(:table => 'mytable', :where => @conds, :prepared => true)
|
146
|
+
assert_equal "delete from mytable #{@wherep.first}", del.first
|
147
|
+
assert_equal @wherep[1..-1], del[1..-1]
|
148
|
+
|
149
|
+
assert_equal "delete from mytable #{@where}",
|
150
|
+
SQLHelper.delete(:table => :mytable, :where => @conds, :prepared => false)
|
151
|
+
end
|
152
|
+
|
153
|
+
def test_update
|
154
|
+
data = { :a => 100, :b => 200, :c => { :sql => 'sysdate' }, :d => "hello 'world'" }
|
155
|
+
sql, *binds = SQLHelper.update(:table => :mytable, :data => data, :where => @conds, :prepared => true)
|
156
|
+
|
157
|
+
assert_equal "update mytable set a = ?, b = ?, c = sysdate, d = ? #{@wherep.first}", sql
|
158
|
+
assert_equal [100, 200, "hello 'world'"] + @wherep[1..-1], binds
|
159
|
+
|
160
|
+
sql = SQLHelper.update(:table => :mytable, :data => data, :where => @conds, :prepared => false)
|
161
|
+
assert_equal "update mytable set a = 100, b = 200, c = sysdate, d = 'hello ''world''' #{@where}", sql
|
162
|
+
end
|
163
|
+
|
164
|
+
def test_check
|
165
|
+
[
|
166
|
+
%['hello],
|
167
|
+
%['hell'o'],
|
168
|
+
%[hel--lo],
|
169
|
+
%[hel;lo],
|
170
|
+
%[hel/*lo],
|
171
|
+
%[hel*/lo],
|
172
|
+
].each do |sql|
|
173
|
+
assert_raises(SyntaxError) { SQLHelper.check sql }
|
174
|
+
assert_raises(SyntaxError) { SQLHelper.project sql }
|
175
|
+
assert_raises(SyntaxError) { SQLHelper.limit sql }
|
176
|
+
assert_raises(SyntaxError) { SQLHelper.order sql }
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
metadata
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sql_helper
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Junegunn Choi
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-06-16 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.3'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.3'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: minitest
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
description: A simplistic SQL generator
|
63
|
+
email:
|
64
|
+
- junegunn.c@gmail.com
|
65
|
+
executables: []
|
66
|
+
extensions: []
|
67
|
+
extra_rdoc_files: []
|
68
|
+
files:
|
69
|
+
- .gitignore
|
70
|
+
- Gemfile
|
71
|
+
- LICENSE.txt
|
72
|
+
- README.md
|
73
|
+
- Rakefile
|
74
|
+
- lib/sql_helper.rb
|
75
|
+
- lib/sql_helper/version.rb
|
76
|
+
- sql_helper.gemspec
|
77
|
+
- test/test_sql_helper.rb
|
78
|
+
homepage: https://github.com/junegunn/sql_helper
|
79
|
+
licenses:
|
80
|
+
- MIT
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options: []
|
83
|
+
require_paths:
|
84
|
+
- lib
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ! '>='
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
segments:
|
92
|
+
- 0
|
93
|
+
hash: 1991983397644293356
|
94
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
|
+
none: false
|
96
|
+
requirements:
|
97
|
+
- - ! '>='
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
segments:
|
101
|
+
- 0
|
102
|
+
hash: 1991983397644293356
|
103
|
+
requirements: []
|
104
|
+
rubyforge_project:
|
105
|
+
rubygems_version: 1.8.25
|
106
|
+
signing_key:
|
107
|
+
specification_version: 3
|
108
|
+
summary: A simplistic SQL generator
|
109
|
+
test_files:
|
110
|
+
- test/test_sql_helper.rb
|