arel 3.0.0 → 3.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +4 -3
- data/History.txt +51 -0
- data/Manifest.txt +8 -0
- data/README.markdown +1 -1
- data/arel.gemspec +27 -16
- data/lib/arel.rb +2 -1
- data/lib/arel/expressions.rb +4 -0
- data/lib/arel/nodes.rb +5 -0
- data/lib/arel/nodes/extract.rb +23 -0
- data/lib/arel/nodes/function.rb +1 -0
- data/lib/arel/nodes/node.rb +1 -1
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/select_core.rb +3 -1
- data/lib/arel/nodes/sql_literal.rb +3 -0
- data/lib/arel/nodes/window.rb +78 -0
- data/lib/arel/select_manager.rb +6 -0
- data/lib/arel/visitors/bind_visitor.rb +24 -0
- data/lib/arel/visitors/depth_first.rb +4 -0
- data/lib/arel/visitors/dot.rb +26 -0
- data/lib/arel/visitors/informix.rb +1 -1
- data/lib/arel/visitors/to_sql.rb +71 -4
- data/lib/arel/window_predications.rb +9 -0
- data/test/nodes/test_extract.rb +19 -0
- data/test/nodes/test_node.rb +1 -0
- data/test/nodes/test_over.rb +49 -0
- data/test/test_select_manager.rb +156 -0
- data/test/visitors/test_bind_visitor.rb +39 -0
- data/test/visitors/test_depth_first.rb +10 -2
- data/test/visitors/test_informix.rb +10 -0
- data/test/visitors/test_oracle.rb +1 -1
- data/test/visitors/test_to_sql.rb +10 -8
- metadata +95 -47
@@ -0,0 +1,24 @@
|
|
1
|
+
module Arel
|
2
|
+
module Visitors
|
3
|
+
module BindVisitor
|
4
|
+
def initialize target
|
5
|
+
@block = nil
|
6
|
+
super
|
7
|
+
end
|
8
|
+
|
9
|
+
def accept node, &block
|
10
|
+
@block = block if block_given?
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
def visit_Arel_Nodes_BindParam o
|
16
|
+
if @block
|
17
|
+
@block.call
|
18
|
+
else
|
19
|
+
super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -70,6 +70,7 @@ module Arel
|
|
70
70
|
alias :visit_Arel_Nodes_GreaterThan :binary
|
71
71
|
alias :visit_Arel_Nodes_GreaterThanOrEqual :binary
|
72
72
|
alias :visit_Arel_Nodes_In :binary
|
73
|
+
alias :visit_Arel_Nodes_InfixOperation :binary
|
73
74
|
alias :visit_Arel_Nodes_JoinSource :binary
|
74
75
|
alias :visit_Arel_Nodes_InnerJoin :binary
|
75
76
|
alias :visit_Arel_Nodes_LessThan :binary
|
@@ -109,6 +110,8 @@ module Arel
|
|
109
110
|
alias :visit_Arel_Nodes_Lock :terminal
|
110
111
|
alias :visit_Arel_Nodes_Node :terminal
|
111
112
|
alias :visit_Arel_Nodes_SqlLiteral :terminal
|
113
|
+
alias :visit_Arel_Nodes_BindParam :terminal
|
114
|
+
alias :visit_Arel_Nodes_Window :terminal
|
112
115
|
alias :visit_Arel_SqlLiteral :terminal
|
113
116
|
alias :visit_BigDecimal :terminal
|
114
117
|
alias :visit_Bignum :terminal
|
@@ -135,6 +138,7 @@ module Arel
|
|
135
138
|
visit o.source
|
136
139
|
visit o.wheres
|
137
140
|
visit o.groups
|
141
|
+
visit o.windows
|
138
142
|
visit o.having
|
139
143
|
end
|
140
144
|
|
data/lib/arel/visitors/dot.rb
CHANGED
@@ -65,6 +65,7 @@ module Arel
|
|
65
65
|
visit_edge o, "expr"
|
66
66
|
end
|
67
67
|
alias :visit_Arel_Nodes_Group :unary
|
68
|
+
alias :visit_Arel_Nodes_BindParam :unary
|
68
69
|
alias :visit_Arel_Nodes_Grouping :unary
|
69
70
|
alias :visit_Arel_Nodes_Having :unary
|
70
71
|
alias :visit_Arel_Nodes_Limit :unary
|
@@ -73,6 +74,23 @@ module Arel
|
|
73
74
|
alias :visit_Arel_Nodes_On :unary
|
74
75
|
alias :visit_Arel_Nodes_Top :unary
|
75
76
|
alias :visit_Arel_Nodes_UnqualifiedColumn :unary
|
77
|
+
alias :visit_Arel_Nodes_Preceding :unary
|
78
|
+
alias :visit_Arel_Nodes_Following :unary
|
79
|
+
alias :visit_Arel_Nodes_Rows :unary
|
80
|
+
alias :visit_Arel_Nodes_Range :unary
|
81
|
+
|
82
|
+
def window o
|
83
|
+
visit_edge o, "orders"
|
84
|
+
visit_edge o, "framing"
|
85
|
+
end
|
86
|
+
alias :visit_Arel_Nodes_Window :window
|
87
|
+
|
88
|
+
def named_window o
|
89
|
+
visit_edge o, "orders"
|
90
|
+
visit_edge o, "framing"
|
91
|
+
visit_edge o, "name"
|
92
|
+
end
|
93
|
+
alias :visit_Arel_Nodes_NamedWindow :named_window
|
76
94
|
|
77
95
|
def function o
|
78
96
|
visit_edge o, "expressions"
|
@@ -85,6 +103,12 @@ module Arel
|
|
85
103
|
alias :visit_Arel_Nodes_Avg :function
|
86
104
|
alias :visit_Arel_Nodes_Sum :function
|
87
105
|
|
106
|
+
def extract o
|
107
|
+
visit_edge o, "expressions"
|
108
|
+
visit_edge o, "alias"
|
109
|
+
end
|
110
|
+
alias :visit_Arel_Nodes_Extract :extract
|
111
|
+
|
88
112
|
def visit_Arel_Nodes_NamedFunction o
|
89
113
|
visit_edge o, "name"
|
90
114
|
visit_edge o, "expressions"
|
@@ -102,6 +126,7 @@ module Arel
|
|
102
126
|
visit_edge o, "source"
|
103
127
|
visit_edge o, "projections"
|
104
128
|
visit_edge o, "wheres"
|
129
|
+
visit_edge o, "windows"
|
105
130
|
end
|
106
131
|
|
107
132
|
def visit_Arel_Nodes_SelectStatement o
|
@@ -158,6 +183,7 @@ module Arel
|
|
158
183
|
alias :visit_Arel_Nodes_NotEqual :binary
|
159
184
|
alias :visit_Arel_Nodes_NotIn :binary
|
160
185
|
alias :visit_Arel_Nodes_Or :binary
|
186
|
+
alias :visit_Arel_Nodes_Over :binary
|
161
187
|
|
162
188
|
def visit_String o
|
163
189
|
@node_stack.last.fields << o
|
@@ -15,7 +15,7 @@ module Arel
|
|
15
15
|
def visit_Arel_Nodes_SelectCore o
|
16
16
|
[
|
17
17
|
"#{o.projections.map { |x| visit x }.join ', '}",
|
18
|
-
("FROM #{visit
|
18
|
+
("FROM #{visit(o.source)}" if o.source && !o.source.empty?),
|
19
19
|
("WHERE #{o.wheres.map { |x| visit x }.join ' AND ' }" unless o.wheres.empty?),
|
20
20
|
("GROUP BY #{o.groups.map { |x| visit x }.join ', ' }" unless o.groups.empty?),
|
21
21
|
(visit(o.having) if o.having),
|
data/lib/arel/visitors/to_sql.rb
CHANGED
@@ -108,7 +108,7 @@ key on UpdateManager using UpdateManager#key=
|
|
108
108
|
def visit_Arel_Nodes_Values o
|
109
109
|
"VALUES (#{o.expressions.zip(o.columns).map { |value, attr|
|
110
110
|
if Nodes::SqlLiteral === value
|
111
|
-
|
111
|
+
visit value
|
112
112
|
else
|
113
113
|
quote(value, attr && column_for(attr))
|
114
114
|
end
|
@@ -133,9 +133,10 @@ key on UpdateManager using UpdateManager#key=
|
|
133
133
|
(visit(o.set_quantifier) if o.set_quantifier),
|
134
134
|
("#{o.projections.map { |x| visit x }.join ', '}" unless o.projections.empty?),
|
135
135
|
("FROM #{visit(o.source)}" if o.source && !o.source.empty?),
|
136
|
-
("WHERE #{o.wheres.map { |x|
|
136
|
+
("WHERE #{o.wheres.map { |x| visit x }.join ' AND ' }" unless o.wheres.empty?),
|
137
137
|
("GROUP BY #{o.groups.map { |x| visit x }.join ', ' }" unless o.groups.empty?),
|
138
138
|
(visit(o.having) if o.having),
|
139
|
+
("WINDOW #{o.windows.map { |x| visit x }.join ', ' }" unless o.windows.empty?)
|
139
140
|
].compact.join ' '
|
140
141
|
end
|
141
142
|
|
@@ -175,6 +176,59 @@ key on UpdateManager using UpdateManager#key=
|
|
175
176
|
"( #{visit o.left} EXCEPT #{visit o.right} )"
|
176
177
|
end
|
177
178
|
|
179
|
+
def visit_Arel_Nodes_NamedWindow o
|
180
|
+
"#{quote_column_name o.name} AS #{visit_Arel_Nodes_Window o}"
|
181
|
+
end
|
182
|
+
|
183
|
+
def visit_Arel_Nodes_Window o
|
184
|
+
s = [
|
185
|
+
("ORDER BY #{o.orders.map { |x| visit(x) }.join(', ')}" unless o.orders.empty?),
|
186
|
+
(visit o.framing if o.framing)
|
187
|
+
].compact.join ' '
|
188
|
+
"(#{s})"
|
189
|
+
end
|
190
|
+
|
191
|
+
def visit_Arel_Nodes_Rows o
|
192
|
+
if o.expr
|
193
|
+
"ROWS #{visit o.expr}"
|
194
|
+
else
|
195
|
+
"ROWS"
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def visit_Arel_Nodes_Range o
|
200
|
+
if o.expr
|
201
|
+
"RANGE #{visit o.expr}"
|
202
|
+
else
|
203
|
+
"RANGE"
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def visit_Arel_Nodes_Preceding o
|
208
|
+
"#{o.expr ? visit(o.expr) : 'UNBOUNDED'} PRECEDING"
|
209
|
+
end
|
210
|
+
|
211
|
+
def visit_Arel_Nodes_Following o
|
212
|
+
"#{o.expr ? visit(o.expr) : 'UNBOUNDED'} FOLLOWING"
|
213
|
+
end
|
214
|
+
|
215
|
+
def visit_Arel_Nodes_CurrentRow o
|
216
|
+
"CURRENT ROW"
|
217
|
+
end
|
218
|
+
|
219
|
+
def visit_Arel_Nodes_Over o
|
220
|
+
case o.right
|
221
|
+
when nil
|
222
|
+
"#{visit o.left} OVER ()"
|
223
|
+
when Arel::Nodes::SqlLiteral
|
224
|
+
"#{visit o.left} OVER #{visit o.right}"
|
225
|
+
when String, Symbol
|
226
|
+
"#{visit o.left} OVER #{quote_column_name o.right.to_s}"
|
227
|
+
else
|
228
|
+
"#{visit o.left} OVER #{visit o.right}"
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
178
232
|
def visit_Arel_Nodes_Having o
|
179
233
|
"HAVING #{visit o.expr}"
|
180
234
|
end
|
@@ -218,6 +272,10 @@ key on UpdateManager using UpdateManager#key=
|
|
218
272
|
}.join(', ')})#{o.alias ? " AS #{visit o.alias}" : ''}"
|
219
273
|
end
|
220
274
|
|
275
|
+
def visit_Arel_Nodes_Extract o
|
276
|
+
"EXTRACT(#{o.field.to_s.upcase} FROM #{visit o.expr})#{o.alias ? " AS #{visit o.alias}" : ''}"
|
277
|
+
end
|
278
|
+
|
221
279
|
def visit_Arel_Nodes_Count o
|
222
280
|
"COUNT(#{o.distinct ? 'DISTINCT ' : ''}#{o.expressions.map { |x|
|
223
281
|
visit x
|
@@ -312,11 +370,19 @@ key on UpdateManager using UpdateManager#key=
|
|
312
370
|
end
|
313
371
|
|
314
372
|
def visit_Arel_Nodes_In o
|
315
|
-
|
373
|
+
if Array === o.right && o.right.empty?
|
374
|
+
'1=0'
|
375
|
+
else
|
376
|
+
"#{visit o.left} IN (#{visit o.right})"
|
377
|
+
end
|
316
378
|
end
|
317
379
|
|
318
380
|
def visit_Arel_Nodes_NotIn o
|
319
|
-
|
381
|
+
if Array === o.right && o.right.empty?
|
382
|
+
'1=1'
|
383
|
+
else
|
384
|
+
"#{visit o.left} NOT IN (#{visit o.right})"
|
385
|
+
end
|
320
386
|
end
|
321
387
|
|
322
388
|
def visit_Arel_Nodes_And o
|
@@ -374,6 +440,7 @@ key on UpdateManager using UpdateManager#key=
|
|
374
440
|
|
375
441
|
def literal o; o end
|
376
442
|
|
443
|
+
alias :visit_Arel_Nodes_BindParam :literal
|
377
444
|
alias :visit_Arel_Nodes_SqlLiteral :literal
|
378
445
|
alias :visit_Arel_SqlLiteral :literal # This is deprecated
|
379
446
|
alias :visit_Bignum :literal
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe Arel::Nodes::Extract do
|
4
|
+
it "should extract field" do
|
5
|
+
table = Arel::Table.new :users
|
6
|
+
table[:timestamp].extract('date').to_sql.must_be_like %{
|
7
|
+
EXTRACT(DATE FROM "users"."timestamp")
|
8
|
+
}
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "as" do
|
12
|
+
it 'should alias the extract' do
|
13
|
+
table = Arel::Table.new :users
|
14
|
+
table[:timestamp].extract('date').as('foo').to_sql.must_be_like %{
|
15
|
+
EXTRACT(DATE FROM "users"."timestamp") AS foo
|
16
|
+
}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/test/nodes/test_node.rb
CHANGED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe Arel::Nodes::Over do
|
4
|
+
describe 'as' do
|
5
|
+
it 'should alias the expression' do
|
6
|
+
table = Arel::Table.new :users
|
7
|
+
table[:id].count.over.as('foo').to_sql.must_be_like %{
|
8
|
+
COUNT("users"."id") OVER () AS foo
|
9
|
+
}
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe 'with literal' do
|
14
|
+
it 'should reference the window definition by name' do
|
15
|
+
table = Arel::Table.new :users
|
16
|
+
table[:id].count.over('foo').to_sql.must_be_like %{
|
17
|
+
COUNT("users"."id") OVER "foo"
|
18
|
+
}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe 'with SQL literal' do
|
23
|
+
it 'should reference the window definition by name' do
|
24
|
+
table = Arel::Table.new :users
|
25
|
+
table[:id].count.over(Arel.sql('foo')).to_sql.must_be_like %{
|
26
|
+
COUNT("users"."id") OVER foo
|
27
|
+
}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe 'with no expression' do
|
32
|
+
it 'should use empty definition' do
|
33
|
+
table = Arel::Table.new :users
|
34
|
+
table[:id].count.over.to_sql.must_be_like %{
|
35
|
+
COUNT("users"."id") OVER ()
|
36
|
+
}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe 'with expression' do
|
41
|
+
it 'should use definition in sub-expression' do
|
42
|
+
table = Arel::Table.new :users
|
43
|
+
window = Arel::Nodes::Window.new.order(table['foo'])
|
44
|
+
table[:id].count.over(window).to_sql.must_be_like %{
|
45
|
+
COUNT("users"."id") OVER (ORDER BY \"users\".\"foo\")
|
46
|
+
}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/test/test_select_manager.rb
CHANGED
@@ -731,6 +731,162 @@ module Arel
|
|
731
731
|
end
|
732
732
|
end
|
733
733
|
|
734
|
+
describe 'window definition' do
|
735
|
+
it 'can be empty' do
|
736
|
+
table = Table.new :users
|
737
|
+
manager = Arel::SelectManager.new Table.engine
|
738
|
+
manager.from table
|
739
|
+
manager.window('a_window')
|
740
|
+
manager.to_sql.must_be_like %{
|
741
|
+
SELECT FROM "users" WINDOW "a_window" AS ()
|
742
|
+
}
|
743
|
+
end
|
744
|
+
|
745
|
+
it 'takes an order' do
|
746
|
+
table = Table.new :users
|
747
|
+
manager = Arel::SelectManager.new Table.engine
|
748
|
+
manager.from table
|
749
|
+
manager.window('a_window').order(table['foo'].asc)
|
750
|
+
manager.to_sql.must_be_like %{
|
751
|
+
SELECT FROM "users" WINDOW "a_window" AS (ORDER BY "users"."foo" ASC)
|
752
|
+
}
|
753
|
+
end
|
754
|
+
|
755
|
+
it 'takes a rows frame, unbounded preceding' do
|
756
|
+
table = Table.new :users
|
757
|
+
manager = Arel::SelectManager.new Table.engine
|
758
|
+
manager.from table
|
759
|
+
manager.window('a_window').rows(Arel::Nodes::Preceding.new)
|
760
|
+
manager.to_sql.must_be_like %{
|
761
|
+
SELECT FROM "users" WINDOW "a_window" AS (ROWS UNBOUNDED PRECEDING)
|
762
|
+
}
|
763
|
+
end
|
764
|
+
|
765
|
+
it 'takes a rows frame, bounded preceding' do
|
766
|
+
table = Table.new :users
|
767
|
+
manager = Arel::SelectManager.new Table.engine
|
768
|
+
manager.from table
|
769
|
+
manager.window('a_window').rows(Arel::Nodes::Preceding.new(5))
|
770
|
+
manager.to_sql.must_be_like %{
|
771
|
+
SELECT FROM "users" WINDOW "a_window" AS (ROWS 5 PRECEDING)
|
772
|
+
}
|
773
|
+
end
|
774
|
+
|
775
|
+
it 'takes a rows frame, unbounded following' do
|
776
|
+
table = Table.new :users
|
777
|
+
manager = Arel::SelectManager.new Table.engine
|
778
|
+
manager.from table
|
779
|
+
manager.window('a_window').rows(Arel::Nodes::Following.new)
|
780
|
+
manager.to_sql.must_be_like %{
|
781
|
+
SELECT FROM "users" WINDOW "a_window" AS (ROWS UNBOUNDED FOLLOWING)
|
782
|
+
}
|
783
|
+
end
|
784
|
+
|
785
|
+
it 'takes a rows frame, bounded following' do
|
786
|
+
table = Table.new :users
|
787
|
+
manager = Arel::SelectManager.new Table.engine
|
788
|
+
manager.from table
|
789
|
+
manager.window('a_window').rows(Arel::Nodes::Following.new(5))
|
790
|
+
manager.to_sql.must_be_like %{
|
791
|
+
SELECT FROM "users" WINDOW "a_window" AS (ROWS 5 FOLLOWING)
|
792
|
+
}
|
793
|
+
end
|
794
|
+
|
795
|
+
it 'takes a rows frame, current row' do
|
796
|
+
table = Table.new :users
|
797
|
+
manager = Arel::SelectManager.new Table.engine
|
798
|
+
manager.from table
|
799
|
+
manager.window('a_window').rows(Arel::Nodes::CurrentRow.new)
|
800
|
+
manager.to_sql.must_be_like %{
|
801
|
+
SELECT FROM "users" WINDOW "a_window" AS (ROWS CURRENT ROW)
|
802
|
+
}
|
803
|
+
end
|
804
|
+
|
805
|
+
it 'takes a rows frame, between two delimiters' do
|
806
|
+
table = Table.new :users
|
807
|
+
manager = Arel::SelectManager.new Table.engine
|
808
|
+
manager.from table
|
809
|
+
window = manager.window('a_window')
|
810
|
+
window.frame(
|
811
|
+
Arel::Nodes::Between.new(
|
812
|
+
window.rows,
|
813
|
+
Nodes::And.new([
|
814
|
+
Arel::Nodes::Preceding.new,
|
815
|
+
Arel::Nodes::CurrentRow.new
|
816
|
+
])))
|
817
|
+
manager.to_sql.must_be_like %{
|
818
|
+
SELECT FROM "users" WINDOW "a_window" AS (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
|
819
|
+
}
|
820
|
+
end
|
821
|
+
|
822
|
+
it 'takes a range frame, unbounded preceding' do
|
823
|
+
table = Table.new :users
|
824
|
+
manager = Arel::SelectManager.new Table.engine
|
825
|
+
manager.from table
|
826
|
+
manager.window('a_window').range(Arel::Nodes::Preceding.new)
|
827
|
+
manager.to_sql.must_be_like %{
|
828
|
+
SELECT FROM "users" WINDOW "a_window" AS (RANGE UNBOUNDED PRECEDING)
|
829
|
+
}
|
830
|
+
end
|
831
|
+
|
832
|
+
it 'takes a range frame, bounded preceding' do
|
833
|
+
table = Table.new :users
|
834
|
+
manager = Arel::SelectManager.new Table.engine
|
835
|
+
manager.from table
|
836
|
+
manager.window('a_window').range(Arel::Nodes::Preceding.new(5))
|
837
|
+
manager.to_sql.must_be_like %{
|
838
|
+
SELECT FROM "users" WINDOW "a_window" AS (RANGE 5 PRECEDING)
|
839
|
+
}
|
840
|
+
end
|
841
|
+
|
842
|
+
it 'takes a range frame, unbounded following' do
|
843
|
+
table = Table.new :users
|
844
|
+
manager = Arel::SelectManager.new Table.engine
|
845
|
+
manager.from table
|
846
|
+
manager.window('a_window').range(Arel::Nodes::Following.new)
|
847
|
+
manager.to_sql.must_be_like %{
|
848
|
+
SELECT FROM "users" WINDOW "a_window" AS (RANGE UNBOUNDED FOLLOWING)
|
849
|
+
}
|
850
|
+
end
|
851
|
+
|
852
|
+
it 'takes a range frame, bounded following' do
|
853
|
+
table = Table.new :users
|
854
|
+
manager = Arel::SelectManager.new Table.engine
|
855
|
+
manager.from table
|
856
|
+
manager.window('a_window').range(Arel::Nodes::Following.new(5))
|
857
|
+
manager.to_sql.must_be_like %{
|
858
|
+
SELECT FROM "users" WINDOW "a_window" AS (RANGE 5 FOLLOWING)
|
859
|
+
}
|
860
|
+
end
|
861
|
+
|
862
|
+
it 'takes a range frame, current row' do
|
863
|
+
table = Table.new :users
|
864
|
+
manager = Arel::SelectManager.new Table.engine
|
865
|
+
manager.from table
|
866
|
+
manager.window('a_window').range(Arel::Nodes::CurrentRow.new)
|
867
|
+
manager.to_sql.must_be_like %{
|
868
|
+
SELECT FROM "users" WINDOW "a_window" AS (RANGE CURRENT ROW)
|
869
|
+
}
|
870
|
+
end
|
871
|
+
|
872
|
+
it 'takes a range frame, between two delimiters' do
|
873
|
+
table = Table.new :users
|
874
|
+
manager = Arel::SelectManager.new Table.engine
|
875
|
+
manager.from table
|
876
|
+
window = manager.window('a_window')
|
877
|
+
window.frame(
|
878
|
+
Arel::Nodes::Between.new(
|
879
|
+
window.range,
|
880
|
+
Nodes::And.new([
|
881
|
+
Arel::Nodes::Preceding.new,
|
882
|
+
Arel::Nodes::CurrentRow.new
|
883
|
+
])))
|
884
|
+
manager.to_sql.must_be_like %{
|
885
|
+
SELECT FROM "users" WINDOW "a_window" AS (RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
|
886
|
+
}
|
887
|
+
end
|
888
|
+
end
|
889
|
+
|
734
890
|
describe 'delete' do
|
735
891
|
it "copies from" do
|
736
892
|
engine = EngineProxy.new Table.engine
|