Skip to content

Commit

Permalink
PostgreSQL lateral expressions
Browse files Browse the repository at this point in the history
Support for PostgreSQL lateral expressions. This is treated as an unary
function applied to a query expression.

Lateral is a separate function to provide interoperability with aliases
and unions. These are also separate node types that wrap
SelectStatements. The lateral option would need to be implemented in
these nodes separately if lateral was an option of SelectStatement.

When building the query, an alias can be given as an argument. This
enables building a lateral query with an table alias without using
either Nodes::TableAlias or Nodes::Lateral directly.
  • Loading branch information
lautis committed Apr 25, 2017
1 parent 6a9f79a commit d8f463a
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 0 deletions.
1 change: 1 addition & 0 deletions lib/arel/nodes/unary.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def eql? other
Group
GroupingElement
GroupingSet
Lateral
Limit
Lock
Not
Expand Down
5 changes: 5 additions & 0 deletions lib/arel/select_manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,11 @@ def except other
end
alias :minus :except

def lateral table_name = nil
base = table_name.nil? ? ast : as(table_name)
Nodes::Lateral.new(base)
end

def with *subqueries
if subqueries.first.is_a? Symbol
node_class = Nodes.const_get("With#{subqueries.shift.to_s.capitalize}")
Expand Down
1 change: 1 addition & 0 deletions lib/arel/visitors/depth_first.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def unary o
alias :visit_Arel_Nodes_GroupingElement :unary
alias :visit_Arel_Nodes_Grouping :unary
alias :visit_Arel_Nodes_Having :unary
alias :visit_Arel_Nodes_Lateral :unary
alias :visit_Arel_Nodes_Limit :unary
alias :visit_Arel_Nodes_Not :unary
alias :visit_Arel_Nodes_Offset :unary
Expand Down
18 changes: 18 additions & 0 deletions lib/arel/visitors/postgresql.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ class PostgreSQL < Arel::Visitors::ToSql
CUBE = 'CUBE'
ROLLUP = 'ROLLUP'
GROUPING_SET = 'GROUPING SET'
LATERAL = 'LATERAL'

private

Expand Down Expand Up @@ -69,6 +70,23 @@ def visit_Arel_Nodes_GroupingSet o, collector
grouping_array_or_grouping_element o, collector
end

def visit_Arel_Nodes_Lateral o, collector
collector << LATERAL
collector << SPACE
grouping_parentheses o, collector
end

# Used by Lateral visitor to enclose select queries in parentheses
def grouping_parentheses o, collector
if o.expr.is_a? Nodes::SelectStatement
collector << "("
visit o.expr, collector
collector << ")"
else
visit o.expr, collector
end
end

# Utilized by GroupingSet, Cube & RollUp visitors to
# handle grouping aggregation semantics
def grouping_array_or_grouping_element o, collector
Expand Down
14 changes: 14 additions & 0 deletions test/visitors/test_postgres.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,20 @@ def compile node
assert_equal 'SELECT DISTINCT', compile(core)
end

it 'encloses LATERAL queries in parens' do
subquery = @table.project(:id).where(@table[:name].matches('foo%'))
compile(subquery.lateral).must_be_like %{
LATERAL (SELECT id FROM "users" WHERE "users"."name" ILIKE 'foo%')
}
end

it 'produces LATERAL queries with alias' do
subquery = @table.project(:id).where(@table[:name].matches('foo%'))
compile(subquery.lateral('bar')).must_be_like %{
LATERAL (SELECT id FROM "users" WHERE "users"."name" ILIKE 'foo%') bar
}
end

describe "Nodes::Matches" do
it "should know how to visit" do
node = @table[:name].matches('foo%')
Expand Down

0 comments on commit d8f463a

Please sign in to comment.