diff options
-rw-r--r-- | lib/arel/collectors/plain_string.rb | 18 | ||||
-rw-r--r-- | lib/arel/collectors/sql_string.rb | 17 | ||||
-rw-r--r-- | lib/arel/nodes/window.rb | 27 | ||||
-rw-r--r-- | lib/arel/tree_manager.rb | 4 | ||||
-rw-r--r-- | lib/arel/visitors/dot.rb | 6 | ||||
-rw-r--r-- | lib/arel/visitors/to_sql.rb | 8 | ||||
-rw-r--r-- | test/nodes/test_window.rb | 12 | ||||
-rw-r--r-- | test/test_insert_manager.rb | 11 | ||||
-rw-r--r-- | test/test_select_manager.rb | 41 | ||||
-rw-r--r-- | test/visitors/test_dot.rb | 8 |
10 files changed, 121 insertions, 31 deletions
diff --git a/lib/arel/collectors/plain_string.rb b/lib/arel/collectors/plain_string.rb new file mode 100644 index 0000000000..2505bc376e --- /dev/null +++ b/lib/arel/collectors/plain_string.rb @@ -0,0 +1,18 @@ +module Arel + module Collectors + class PlainString + def initialize + @str = '' + end + + def value + @str + end + + def << str + @str << str + self + end + end + end +end diff --git a/lib/arel/collectors/sql_string.rb b/lib/arel/collectors/sql_string.rb index 45001bb507..8ca89ca7bd 100644 --- a/lib/arel/collectors/sql_string.rb +++ b/lib/arel/collectors/sql_string.rb @@ -1,21 +1,10 @@ # encoding: utf-8 +require 'arel/collectors/plain_string' + module Arel module Collectors - class SQLString - def initialize - @str = '' - end - - def value - @str - end - - def << str - @str << str - self - end - + class SQLString < PlainString def add_bind bind self << bind.to_s self diff --git a/lib/arel/nodes/window.rb b/lib/arel/nodes/window.rb index 60259e8c05..fee8eeff7a 100644 --- a/lib/arel/nodes/window.rb +++ b/lib/arel/nodes/window.rb @@ -1,10 +1,12 @@ module Arel module Nodes class Window < Arel::Nodes::Node - attr_accessor :orders, :framing + attr_accessor :orders, :framing, :partitions def initialize @orders = [] + @partitions = [] + @framing = nil end def order *expr @@ -15,16 +17,32 @@ module Arel self end + def partition *expr + # FIXME: We SHOULD NOT be converting these to SqlLiteral automatically + @partitions.concat expr.map { |x| + String === x || Symbol === x ? Nodes::SqlLiteral.new(x.to_s) : x + } + self + end + def frame(expr) @framing = expr end def rows(expr = nil) - frame(Rows.new(expr)) + if @framing + Rows.new(expr) + else + frame(Rows.new(expr)) + end end def range(expr = nil) - frame(Range.new(expr)) + if @framing + Range.new(expr) + else + frame(Range.new(expr)) + end end def initialize_copy other @@ -39,7 +57,8 @@ module Arel def eql? other self.class == other.class && self.orders == other.orders && - self.framing == other.framing + self.framing == other.framing && + self.partitions == other.partitions end alias :== :eql? end diff --git a/lib/arel/tree_manager.rb b/lib/arel/tree_manager.rb index 87887800d0..8bff97af78 100644 --- a/lib/arel/tree_manager.rb +++ b/lib/arel/tree_manager.rb @@ -15,7 +15,9 @@ module Arel end def to_dot - Visitors::Dot.new.accept @ast + collector = Arel::Collectors::PlainString.new + collector = Visitors::Dot.new.accept @ast, collector + collector.value end def visitor diff --git a/lib/arel/visitors/dot.rb b/lib/arel/visitors/dot.rb index ba35223ac9..f0cefeabd7 100644 --- a/lib/arel/visitors/dot.rb +++ b/lib/arel/visitors/dot.rb @@ -22,9 +22,9 @@ module Arel @seen = {} end - def accept object + def accept object, collector visit object - to_dot + collector << to_dot end private @@ -82,12 +82,14 @@ module Arel alias :visit_Arel_Nodes_Range :unary def window o + visit_edge o, "partitions" visit_edge o, "orders" visit_edge o, "framing" end alias :visit_Arel_Nodes_Window :window def named_window o + visit_edge o, "partitions" visit_edge o, "orders" visit_edge o, "framing" visit_edge o, "name" diff --git a/lib/arel/visitors/to_sql.rb b/lib/arel/visitors/to_sql.rb index 7f74ebb402..8c63070084 100644 --- a/lib/arel/visitors/to_sql.rb +++ b/lib/arel/visitors/to_sql.rb @@ -336,12 +336,20 @@ module Arel def visit_Arel_Nodes_Window o, collector collector << "(" + + if o.partitions.any? + collector << "PARTITION BY " + collector = inject_join o.partitions, collector, ", " + end + if o.orders.any? + collector << ' ' if o.partitions.any? collector << "ORDER BY " collector = inject_join o.orders, collector, ", " end if o.framing + collector << ' ' if o.partitions.any? or o.orders.any? collector = visit o.framing, collector end diff --git a/test/nodes/test_window.rb b/test/nodes/test_window.rb index f09d16e441..9ec42be59f 100644 --- a/test/nodes/test_window.rb +++ b/test/nodes/test_window.rb @@ -7,9 +7,11 @@ module Arel it 'is equal with equal ivars' do window1 = Window.new window1.orders = [1, 2] + window1.partitions = [1] window1.frame 3 window2 = Window.new window2.orders = [1, 2] + window2.partitions = [1] window2.frame 3 array = [window1, window2] assert_equal 1, array.uniq.size @@ -18,9 +20,11 @@ module Arel it 'is not equal with different ivars' do window1 = Window.new window1.orders = [1, 2] + window1.partitions = [1] window1.frame 3 window2 = Window.new window2.orders = [1, 2] + window1.partitions = [1] window2.frame 4 array = [window1, window2] assert_equal 2, array.uniq.size @@ -33,9 +37,11 @@ module Arel it 'is equal with equal ivars' do window1 = NamedWindow.new 'foo' window1.orders = [1, 2] + window1.partitions = [1] window1.frame 3 window2 = NamedWindow.new 'foo' window2.orders = [1, 2] + window2.partitions = [1] window2.frame 3 array = [window1, window2] assert_equal 1, array.uniq.size @@ -44,9 +50,11 @@ module Arel it 'is not equal with different ivars' do window1 = NamedWindow.new 'foo' window1.orders = [1, 2] + window1.partitions = [1] window1.frame 3 window2 = NamedWindow.new 'bar' window2.orders = [1, 2] + window2.partitions = [1] window2.frame 3 array = [window1, window2] assert_equal 2, array.uniq.size @@ -68,6 +76,4 @@ module Arel end end end -end - - +end
\ No newline at end of file diff --git a/test/test_insert_manager.rb b/test/test_insert_manager.rb index 4e82ca34c0..9cfd01262b 100644 --- a/test/test_insert_manager.rb +++ b/test/test_insert_manager.rb @@ -78,14 +78,19 @@ module Arel } end - it 'takes an empty list' do + it 'noop for empty list' do + table = Table.new(:users) manager = Arel::InsertManager.new Table.engine + manager.insert [[table[:id], 1]] manager.insert [] + manager.to_sql.must_be_like %{ + INSERT INTO "users" ("id") VALUES (1) + } end end describe 'into' do - it 'takes an engine' do + it 'takes a Table and chains' do manager = Arel::InsertManager.new Table.engine manager.into(Table.new(:users)).must_equal manager end @@ -126,7 +131,7 @@ module Arel end describe "combo" do - it "puts shit together" do + it "combines columns and values list in order" do table = Table.new :users manager = Arel::InsertManager.new Table.engine manager.into table diff --git a/test/test_select_manager.rb b/test/test_select_manager.rb index 8880e325e4..3380bbec6f 100644 --- a/test/test_select_manager.rb +++ b/test/test_select_manager.rb @@ -732,6 +732,47 @@ module Arel } end + it 'takes an order with multiple columns' do + table = Table.new :users + manager = Arel::SelectManager.new Table.engine + manager.from table + manager.window('a_window').order(table['foo'].asc, table['bar'].desc) + manager.to_sql.must_be_like %{ + SELECT FROM "users" WINDOW "a_window" AS (ORDER BY "users"."foo" ASC, "users"."bar" DESC) + } + end + + it 'takes a partition' do + table = Table.new :users + manager = Arel::SelectManager.new Table.engine + manager.from table + manager.window('a_window').partition(table['bar']) + manager.to_sql.must_be_like %{ + SELECT FROM "users" WINDOW "a_window" AS (PARTITION BY "users"."bar") + } + end + + it 'takes a partition and an order' do + table = Table.new :users + manager = Arel::SelectManager.new Table.engine + manager.from table + manager.window('a_window').partition(table['foo']).order(table['foo'].asc) + manager.to_sql.must_be_like %{ + SELECT FROM "users" WINDOW "a_window" AS (PARTITION BY "users"."foo" + ORDER BY "users"."foo" ASC) + } + end + + it 'takes a partition with multiple columns' do + table = Table.new :users + manager = Arel::SelectManager.new Table.engine + manager.from table + manager.window('a_window').partition(table['bar'], table['baz']) + manager.to_sql.must_be_like %{ + SELECT FROM "users" WINDOW "a_window" AS (PARTITION BY "users"."bar", "users"."baz") + } + end + it 'takes a rows frame, unbounded preceding' do table = Table.new :users manager = Arel::SelectManager.new Table.engine diff --git a/test/visitors/test_dot.rb b/test/visitors/test_dot.rb index ee7fc7886c..7763350f5c 100644 --- a/test/visitors/test_dot.rb +++ b/test/visitors/test_dot.rb @@ -17,13 +17,13 @@ module Arel ].each do |klass| define_method("test_#{klass.name.gsub('::', '_')}") do op = klass.new(:a, "z") - @visitor.accept op + @visitor.accept op, Collectors::PlainString.new end end def test_named_function func = Nodes::NamedFunction.new 'omg', 'omg' - @visitor.accept func + @visitor.accept func, Collectors::PlainString.new end # unary ops @@ -41,7 +41,7 @@ module Arel ].each do |klass| define_method("test_#{klass.name.gsub('::', '_')}") do op = klass.new(:a) - @visitor.accept op + @visitor.accept op, Collectors::PlainString.new end end @@ -68,7 +68,7 @@ module Arel ].each do |klass| define_method("test_#{klass.name.gsub('::', '_')}") do binary = klass.new(:a, :b) - @visitor.accept binary + @visitor.accept binary, Collectors::PlainString.new end end end |