arel 3.0.0 → 3.0.3
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/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
|