sql_helper 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|