sequel_core 2.1.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,50 +0,0 @@
1
- unless defined?(SEQUEL_NO_PARSE_TREE)
2
- begin
3
- require 'parse_tree'
4
- require 'sequel_core/dataset/parse_tree_sequelizer'
5
- class Proc
6
- def to_sql(dataset, opts = {})
7
- Sequel::Deprecation.deprecate("ParseTree filters are deprecated and will be removed in Sequel 2.2")
8
- dataset.send(:pt_expr, to_sexp[2], self.binding, opts)
9
- end
10
- end
11
- begin
12
- require 'ruby2ruby'
13
- class Sequel::Dataset
14
- # Evaluates a method call. This method is used to evaluate Ruby expressions
15
- # referring to indirect values, e.g.:
16
- #
17
- # dataset.filter {:category => category.to_s}
18
- # dataset.filter {:x > y[0..3]}
19
- #
20
- # This method depends on the Ruby2Ruby gem. If you do not have Ruby2Ruby
21
- # installed, this method will raise an error.
22
- def ext_expr(e, b, opts)
23
- eval(::RubyToRuby.new.process(e), b)
24
- end
25
- end
26
- class Proc
27
- remove_method :to_sexp
28
- end
29
- rescue LoadError
30
- class Sequel::Dataset
31
- def ext_expr(*args)
32
- raise Sequel::Error, "You must have the Ruby2Ruby gem installed in order to use this block filter."
33
- end
34
- end
35
- ensure
36
- class Proc
37
- # replacement for Proc#to_sexp as defined in ruby2ruby.
38
- # see also: http://rubyforge.org/tracker/index.php?func=detail&aid=18095&group_id=1513&atid=5921
39
- # The ruby2ruby implementation leaks memory, so we fix it.
40
- def to_sexp
41
- block = self
42
- c = Class.new {define_method(:m, &block)}
43
- ParseTree.translate(c, :m)[2]
44
- end
45
- end
46
- end
47
- rescue LoadError
48
- SEQUEL_NO_PARSE_TREE = true
49
- end
50
- end
@@ -1,504 +0,0 @@
1
- require File.join(File.dirname(__FILE__), 'spec_helper')
2
-
3
- context "Sequelizer without ParseTree" do
4
- setup do
5
- Sequel.use_parse_tree = false
6
- @db = Sequel::Database.new
7
- @ds = @db[:items]
8
- end
9
-
10
- teardown do
11
- Sequel.use_parse_tree = true
12
- end
13
-
14
- specify "should raise error when using ParseTree specific syntax without ParseTree" do
15
- proc {@ds.filter{:x << 1}}.should raise_error
16
- end
17
-
18
- specify "should allow expression syntax inside block raise error when converting ParseTree proc to SQL" do
19
- proc {@ds.filter{:x > 1}}.should_not raise_error(Sequel::Error)
20
- end
21
- end
22
-
23
- context "Sequelizer without Ruby2Ruby" do
24
- setup do
25
- module Kernel
26
- alias_method :orig_sq_require, :require
27
- def require(name); raise LoadError if name == 'ruby2ruby'; end
28
- end
29
- old_verbose = $VERBOSE
30
- $VERBOSE = nil
31
- load(File.join(File.dirname(__FILE__), '../lib/sequel_core/dataset/sequelizer.rb'))
32
- $VERBOSE = old_verbose
33
- @db = Sequel::Database.new
34
- @ds = @db[:items]
35
- end
36
-
37
- teardown do
38
- module Kernel
39
- alias_method :require, :orig_sq_require
40
- end
41
- old_verbose = $VERBOSE
42
- $VERBOSE = nil
43
- load(File.join(File.dirname(__FILE__), '../lib/sequel_core/dataset/sequelizer.rb'))
44
- $VERBOSE = old_verbose
45
- end
46
-
47
- pt_specify "should raise error only when using external expressions" do
48
- proc {proc {:x > 1}.to_sql(@ds)}.should_not raise_error(Sequel::Error)
49
- proc {proc {1 + 1}.to_sql(@ds)}.should raise_error(Sequel::Error)
50
- end
51
- end
52
-
53
- context "Proc#to_sql" do
54
- DB = Sequel::Database.new
55
- DS = DB[:items]
56
-
57
- class ::Proc
58
- def sql
59
- to_sql(DS)
60
- end
61
-
62
- def sql_comma_separated
63
- to_sql(DS, :comma_separated => true)
64
- end
65
- end
66
-
67
- def DS.match_expr(l, r)
68
- case r
69
- when String
70
- "(#{literal(l)} LIKE #{literal(r)})"
71
- when Regexp
72
- "(#{literal(l)} ~ #{literal(r.source)})"
73
- else
74
- raise Sequel::Error, "Unsupported match pattern class (#{r.class})."
75
- end
76
- end
77
-
78
- pt_specify "should support <sym> <op> <lit>" do
79
- proc {:x > 100}.sql.should == '(x > 100)'
80
- proc {:x < 100}.sql.should == '(x < 100)'
81
- proc {:x >= 100}.sql.should == '(x >= 100)'
82
- proc {:x <= 100}.sql.should == '(x <= 100)'
83
- proc {:x == 100}.sql.should == '(x = 100)'
84
- end
85
-
86
- pt_specify "should support number literals" do
87
- proc {:x > 123.45}.sql.should == '(x > 123.45)'
88
- proc {:x > -30_000}.sql.should == '(x > -30000)'
89
- end
90
-
91
- pt_specify "should support string literals" do
92
- proc {:x == 'abc'}.sql.should == "(x = 'abc')"
93
- proc {:y == "ab'cd"}.sql.should == "(y = 'ab''cd')"
94
- end
95
-
96
- pt_specify "should support boolean literals" do
97
- proc {:x == false}.sql.should == "(x = 'f')"
98
- proc {:x == true}.sql.should == "(x = 't')"
99
- end
100
-
101
- pt_specify "should support nil literal and nil?" do
102
- proc {:x == nil}.sql.should == "(x IS NULL)"
103
- proc {:x.nil?}.sql.should == "(x IS NULL)"
104
- end
105
-
106
- pt_specify "should support local vars or method references" do
107
- proc {proc {:x == a}.sql}.should raise_error(NameError)
108
- b = 123
109
- proc {:x == b}.sql.should == "(x = 123)"
110
- def xyz; 321; end
111
- proc {:x == xyz}.sql.should == "(x = 321)"
112
- proc {:x == xyz.to_s}.sql.should == "(x = '321')"
113
-
114
- def y1(x); x; end
115
- def y2; 111; end
116
-
117
- proc {:x == y1(222)}.sql.should == "(x = 222)"
118
- proc {:x == y2}.sql.should == "(x = 111)"
119
- end
120
-
121
- pt_specify "sould support subscript access on symbols" do
122
- proc {:x|1 > 0}.sql.should == "(x[1] > 0)"
123
- proc {:x|2|3 > 0}.sql.should == "(x[2, 3] > 0)"
124
- proc {:x|[4, 5] > 0}.sql.should == "(x[4, 5] > 0)"
125
- end
126
-
127
- pt_specify "should support constants" do
128
- ZZZ = 444
129
- proc {:x == ZZZ}.sql.should == "(x = 444)"
130
-
131
- CCCD = Module.new
132
- CCCD::DDD = 'hi'
133
- proc {:x == CCCD::DDD}.sql.should == "(x = 'hi')"
134
- end
135
-
136
- pt_specify "should support instance attributes" do
137
- @abc = 123
138
- proc {:x == @abc}.sql.should == "(x = 123)"
139
- end
140
-
141
- pt_specify "should support class attributes" do
142
- @@abc = 321
143
- proc {:x == @@abc}.sql.should == "(x = 321)"
144
- end
145
-
146
- pt_specify "should support like? pattern" do
147
- proc {:x.like? '%abc'}.sql.should == "(x LIKE '%abc')"
148
- end
149
-
150
- pt_specify "should support like? pattern with multiple choices" do
151
- proc {:x.like? ['%abc', '%def', '%ghi']}.sql.should == \
152
- "((x LIKE '%abc') OR (x LIKE '%def') OR (x LIKE '%ghi'))"
153
- end
154
-
155
- pt_specify "should support =~ operator" do
156
- # stock SQL version does not know about regexps
157
- proc {:x =~ '123'}.sql.should == "(x LIKE '123')"
158
-
159
- proc {:x =~ /^123/}.sql.should == "(x ~ '^123')"
160
- end
161
-
162
- pt_specify "should support =~ operator with multiple choices" do
163
- # stock SQL version does not know about regexps
164
- proc {:x =~ ['123', '456', '789']}.sql.should == "((x LIKE '123') OR (x LIKE '456') OR (x LIKE '789'))"
165
-
166
- proc {:x =~ [/^123/, /^456/, /^789/]}.sql.should == "((x ~ '^123') OR (x ~ '^456') OR (x ~ '^789'))"
167
-
168
- proc {:x =~ [/^123/, '456%', /^789/]}.sql.should == "((x ~ '^123') OR (x LIKE '456%') OR (x ~ '^789'))"
169
- end
170
-
171
- pt_specify "should raise on =~ operator for unsupported types" do
172
- proc {proc {:x =~ 123}.sql}.should raise_error(Sequel::Error)
173
- end
174
-
175
- pt_specify "should support != operator" do
176
- proc {:x != 100}.sql.should == "(NOT (x = 100))"
177
- end
178
-
179
- pt_specify "should support != operator with multiple choices" do
180
- proc {:x != [100, 200, 300]}.sql.should == "(NOT (x IN (100, 200, 300)))"
181
- end
182
-
183
- pt_specify "should support !~ operator" do
184
- proc {:x !~ '123'}.sql.should == "(NOT (x LIKE '123'))"
185
- end
186
-
187
- pt_specify "should support !~ operator with multiple choices" do
188
- proc {:x !~ ['123', '456']}.sql.should == "(NOT ((x LIKE '123') OR (x LIKE '456')))"
189
- end
190
-
191
- pt_specify "should support ! operator" do
192
- proc {!:x}.sql.should == "(x = 'f')"
193
- proc {!(:x > 100)}.sql.should == "(NOT (x > 100))"
194
- end
195
-
196
- pt_specify "should support && operator" do
197
- proc {1 && 2}.sql.should == "(1 AND 2)"
198
- proc {:x > 100 && :y < 100}.sql.should == "((x > 100) AND (y < 100))"
199
- proc {:x && :y && :z}.sql.should == "(x AND (y AND z))"
200
- end
201
-
202
- pt_specify "should support << operator for assignment" do
203
- proc {:x << 1}.sql.should == "x = 1"
204
- end
205
-
206
- pt_specify "should concatenate separate statements using AND" do
207
- proc {:x == 20; :y == 30}.sql.should == "((x = 20) AND (y = 30))"
208
- proc {:x != 1; :y != 2; :z != 3}.sql.should == \
209
- "((NOT (x = 1)) AND (NOT (y = 2)) AND (NOT (z = 3)))"
210
- end
211
-
212
- pt_specify "should concatenate separate statements using custom join argument" do
213
- proc {:x << 20; :y << 30}.sql_comma_separated.should == "x = 20, y = 30"
214
- z = 333
215
- proc {:x << :x + 1; :y << z}.sql_comma_separated.should == "x = (x + 1), y = 333"
216
- end
217
-
218
- pt_specify "should support || operator" do
219
- proc {1 || 2}.sql.should == "(1 OR 2)"
220
- proc {:x > 100 || :y < 100}.sql.should == "((x > 100) OR (y < 100))"
221
- proc {:x || :y || :z}.sql.should == "(x OR (y OR z))"
222
- end
223
-
224
- pt_specify "should support operator combinations" do
225
- proc {(:x > 1 || :y > 2) && (:z > 3)}.sql.should == "(((x > 1) OR (y > 2)) AND (z > 3))"
226
- proc {(1 && 2) || (3 || 4)}.sql.should == "((1 AND 2) OR (3 OR 4))"
227
- proc {(:x != 2) || (:y == 3) || !(:z == 4)}.sql.should == \
228
- "((NOT (x = 2)) OR ((y = 3) OR (NOT (z = 4))))"
229
- end
230
-
231
- pt_specify "should support late bound column references" do
232
- def abc; :tttt; end
233
- proc {abc > 2}.sql.should == "(tttt > 2)"
234
- end
235
-
236
- pt_specify "should support qualified column references" do
237
- proc {:x__y > 3}.sql.should == "(x.y > 3)"
238
- end
239
-
240
- pt_specify "should support functions on columns" do
241
- proc {:max[:x] > 100}.sql.should == "(max(x) > 100)"
242
- proc {:count[:x] > 100}.sql.should == "(count(x) > 100)"
243
- end
244
-
245
- pt_specify "should support SQL functions" do
246
- proc {:MAX[:x] > 100}.sql.should == "(MAX(x) > 100)"
247
-
248
- proc {:MAX[:x__y] > 100}.sql.should == "(MAX(x.y) > 100)"
249
- end
250
-
251
- pt_specify "should support SQL functions with multiple arguments" do
252
- proc {:sum[1, 2, 3] > 100}.sql.should == "(sum(1, 2, 3) > 100)"
253
-
254
- proc {:x[1, DB[:y].select(:z), "a'b"] > 100}.sql.should == \
255
- "(x(1, (SELECT z FROM y), 'a''b') > 100)"
256
- end
257
-
258
- pt_specify "should support SQL functions without arguments" do
259
- proc {:abc[] > 100}.sql.should == "(abc() > 100)"
260
-
261
- proc {:now[] - :last_stamp > 100}.sql.should == \
262
- "((now() - last_stamp) > 100)"
263
- end
264
-
265
- pt_specify "should do stuff like..." do
266
- proc {:price < 100 || :category != 'ruby'}.sql.should == \
267
- "((price < 100) OR (NOT (category = 'ruby')))"
268
- t = Time.now
269
- proc {:node_id == 1 && :stamp < t}.sql.should == \
270
- "((node_id = 1) AND (stamp < #{DS.literal(t)}))"
271
-
272
- proc {1 < :x}.sql.should == "(1 < x)"
273
- end
274
-
275
- pt_specify "should complain if someone is crazy" do
276
- proc {proc {def x; 1; end}.sql}.should raise_error(Sequel::Error::InvalidExpression)
277
- a = 1
278
- proc {proc {a = 1}.sql}.should raise_error(Sequel::Error::InvalidExpression)
279
- end
280
-
281
- pt_specify "should support comparison to Range objects" do
282
- proc {:x == (1..10)}.sql.should == \
283
- "(x >= 1 AND x <= 10)"
284
-
285
- proc {:x == (1...10)}.sql.should == \
286
- "(x >= 1 AND x < 10)"
287
-
288
- a, b = 3, 5
289
- proc {:x == (a..b)}.sql.should == \
290
- "(x >= 3 AND x <= 5)"
291
-
292
- proc {:x == (a...b)}.sql.should == \
293
- "(x >= 3 AND x < 5)"
294
-
295
- t1 = Time.now - 4000
296
- t2 = Time.now - 2000
297
-
298
- proc {:stamp == (t1..t2)}.sql.should == \
299
- "(stamp >= #{DS.literal(t1)} AND stamp <= #{DS.literal(t2)})"
300
- end
301
-
302
- pt_specify "should support comparison to sub-queries" do
303
- @ds2 = DB[:test].select(:node_id)
304
-
305
- proc {:id == @ds2}.sql.should == \
306
- "(id IN (SELECT node_id FROM test))"
307
-
308
- proc {:id == DB[:test].select(:node_id)}.sql.should == \
309
- "(id IN (SELECT node_id FROM test))"
310
-
311
- proc {:id == DB[:test].select(:node_id).filter {:active == true}}.sql.should == \
312
- "(id IN (SELECT node_id FROM test WHERE (active = 't')))"
313
-
314
- proc {:price >= DB[:items].select(:price)}.sql.should == \
315
- "(price >= (SELECT price FROM items))"
316
- end
317
-
318
- pt_specify "should support comparison to arrays" do
319
- proc {:id == [1, 3, 7, 15]}.sql.should == \
320
- "(id IN (1, 3, 7, 15))"
321
- end
322
-
323
- pt_specify "should not literalize String#expr and String#lit" do
324
- proc {'x'.lit == 1}.sql.should == "(x = 1)"
325
- proc {'x.y'.expr == 1}.sql.should == "(x.y = 1)"
326
- end
327
-
328
- pt_specify "should support in/in? operator" do
329
- proc {:x.in [3, 4, 5]}.sql.should == "(x IN (3, 4, 5))"
330
- proc {:x.in?(3, 4, 5)}.sql.should == "(x IN (3, 4, 5))"
331
-
332
- proc {:x.in(1..10)}.sql.should == "(x >= 1 AND x <= 10)"
333
- proc {:x.in?(1..10)}.sql.should == "(x >= 1 AND x <= 10)"
334
-
335
- @ds2 = DB[:test].select(:node_id)
336
- proc {:x.in @ds2}.sql.should == "(x IN (SELECT node_id FROM test))"
337
- end
338
-
339
- pt_specify "should support nested procs" do
340
- proc {:x > 10 || proc{:y > 20}}.sql.should == \
341
- "((x > 10) OR (y > 20))"
342
-
343
- def pr(&block)
344
- proc {:x > 10 || block}
345
- end
346
-
347
- pr {:y > 20}.sql.should == \
348
- "((x > 10) OR (y > 20))"
349
- end
350
-
351
- pt_specify "should support unfolding of calls to #each" do
352
- # from http://groups.google.com/group/sequel-talk/browse_thread/thread/54a660568515fbb7
353
- periods = [:day, :week, :month, :year, :alltime]
354
- idx = 1
355
- v = 2
356
- pr = proc do
357
- periods.each do |p|
358
- (p|idx) << (p|idx) + v
359
- end
360
- end
361
- pr.sql_comma_separated.should == \
362
- "day[1] = (day[1] + 2), week[1] = (week[1] + 2), month[1] = (month[1] + 2), year[1] = (year[1] + 2), alltime[1] = (alltime[1] + 2)"
363
- end
364
-
365
- pt_specify "should support unfolding of calls to Hash#each" do
366
- periods = {:month => 3}
367
- idx = 1
368
- pr = proc do
369
- periods.each do |k, v|
370
- k << k + v
371
- end
372
- end
373
- pr.sql_comma_separated.should == "month = (month + 3)"
374
- end
375
-
376
- pt_specify "should support local arguments" do
377
- def t(x)
378
- proc {x > 10}.sql
379
- end
380
- t(:y).should == "(y > 10)"
381
- end
382
-
383
- pt_specify "should support binary operators on local context" do
384
- XXX = 1
385
- YYY = 2
386
- proc {XXX || YYY}.sql.should == "(1 OR 2)"
387
-
388
- xxx = 1
389
- yyy = 2
390
- proc {xxx && yyy}.sql.should == "(1 AND 2)"
391
- end
392
-
393
- pt_specify "should support arithmetics" do
394
- zzz = 300
395
- proc {(:x + 100) > zzz}.sql.should == "((x + 100) > 300)"
396
-
397
- proc {(:x + :y * 100) > zzz}.sql.should == "((x + (y * 100)) > 300)"
398
-
399
- proc {:units * :price}.sql.should == "(units * price)"
400
- end
401
-
402
- pt_specify "should support | operator" do
403
- proc {(:x | 1) > 0}.sql.should == "(x[1] > 0)"
404
- proc {10 | 1}.sql.should == 11
405
- end
406
-
407
- pt_specify "should support globals" do
408
- $aaaa_zzzz = 400
409
- proc {:x > $aaaa_zzzz}.sql.should == "(x > 400)"
410
- end
411
-
412
- pt_specify "should support Regexp macros" do
413
- "abc" =~ /(ab)/
414
- proc {:x == $1}.sql.should == "(x = 'ab')"
415
- end
416
-
417
- pt_specify "should evaluate expression not referring to symbols or literal strings." do
418
- proc {:x > 2 * 3}.sql.should == "(x > 6)"
419
- y = 3
420
- proc {:x > y * 4}.sql.should == "(x > 12)"
421
-
422
- proc {:AVG[:x] > 4}.sql.should == "(AVG(x) > 4)"
423
-
424
- proc {:AVG[:x] > 4}.sql.should == "(AVG(x) > 4)"
425
-
426
- proc {:y == (1 > 2)}.sql.should == "(y = 'f')"
427
- end
428
-
429
- pt_specify "should support ternary operator" do
430
- y = true
431
- proc {:x > (y ? 1 : 2)}.sql.should == "(x > 1)"
432
-
433
- proc {((1 > 2) ? :x : :y) > 3}.sql.should == "(y > 3)"
434
- end
435
-
436
- pt_specify "should support strings with embedded Ruby code in them and literalize them" do
437
- proc {:n == "#{1+2}"}.sql.should == "(n = '3')"
438
-
439
- y = "12'34"
440
-
441
- proc {:x > "#{y}"}.sql.should == "(x > '12''34')"
442
- end
443
-
444
- pt_specify "should support format strings and literalize the result" do
445
- prod = 1
446
- proc {:x == "abc%d" % prod}.sql.should == "(x = 'abc1')"
447
-
448
- proc {:x == ("%d" % prod).lit}.sql.should == "(x = 1)"
449
- end
450
-
451
- pt_specify "should support conditional filters" do
452
- @criteria = nil
453
- proc {if @criteria; :x.like @criteria; end}.sql.should == nil
454
-
455
- @criteria = 'blah'
456
- proc {if @criteria; :x.like @criteria; end}.sql.should == "(x LIKE 'blah')"
457
-
458
- @criteria = nil
459
- proc {if @criteria; :x.like @criteria; else; :x.like 'ddd'; end}.sql.should == "(x LIKE 'ddd')"
460
- end
461
-
462
- pt_specify "should support more complex conditional filters" do
463
- x, y = true, false
464
- proc {
465
- if x || y
466
- :a.like 'a'
467
- else
468
- :b.like 'b'
469
- end
470
- }.sql.should == "(a LIKE 'a')"
471
-
472
- proc {
473
- if y
474
- :a.like 'a'
475
- elsif x
476
- :b.like 'b'
477
- else
478
- :c.like 'c'
479
- end
480
- }.sql.should == "(b LIKE 'b')"
481
-
482
- @title = 'blah'
483
- use_title = true
484
-
485
- proc {
486
- if !@title.blank? && use_title
487
- :title.like "%#{@title}%"
488
- end
489
- }.sql.should == "(title LIKE '%blah%')"
490
-
491
-
492
- end
493
- end
494
-
495
- context "Proc#to_sql stock" do
496
- pt_specify "should not support regexps" do
497
- db = Sequel::Database.new
498
- ds = db[:items]
499
-
500
- p = proc {:x =~ /abc/}
501
- proc {p.to_sql(ds)}.should raise_error(Sequel::Error)
502
- end
503
- end
504
-