aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/arel/collectors/plain_string.rb18
-rw-r--r--lib/arel/collectors/sql_string.rb17
-rw-r--r--lib/arel/nodes/window.rb27
-rw-r--r--lib/arel/tree_manager.rb4
-rw-r--r--lib/arel/visitors/dot.rb6
-rw-r--r--lib/arel/visitors/to_sql.rb8
-rw-r--r--test/nodes/test_window.rb12
-rw-r--r--test/test_insert_manager.rb11
-rw-r--r--test/test_select_manager.rb41
-rw-r--r--test/visitors/test_dot.rb8
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