diff --git a/activerecord/lib/arel/nodes.rb b/activerecord/lib/arel/nodes.rb index f994754620061f50c047bb2667626dae6d1b7afa..e7c1acc30a755a0eceb7627ccb8ecec35a9abd38 100644 --- a/activerecord/lib/arel/nodes.rb +++ b/activerecord/lib/arel/nodes.rb @@ -18,6 +18,7 @@ # unary require "arel/nodes/unary" require "arel/nodes/grouping" +require "arel/nodes/ordering" require "arel/nodes/ascending" require "arel/nodes/descending" require "arel/nodes/unqualified_column" diff --git a/activerecord/lib/arel/nodes/ordering.rb b/activerecord/lib/arel/nodes/ordering.rb new file mode 100644 index 0000000000000000000000000000000000000000..4c4cb58c9f8895aba2cf773cd72f3a57672df067 --- /dev/null +++ b/activerecord/lib/arel/nodes/ordering.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + module Nodes + class Ordering < Unary + def nulls_first + NullsFirst.new(self) + end + + def nulls_last + NullsLast.new(self) + end + end + + class NullsFirst < Ordering; end + class NullsLast < Ordering; end + end +end diff --git a/activerecord/lib/arel/nodes/unary.rb b/activerecord/lib/arel/nodes/unary.rb index 6d1ac36b0ee9a7a11e135b100b3f75f95d2d8296..cf1eca283197305412845693227ebda1855802e6 100644 --- a/activerecord/lib/arel/nodes/unary.rb +++ b/activerecord/lib/arel/nodes/unary.rb @@ -36,7 +36,6 @@ def eql?(other) Offset On OptimizerHints - Ordering RollUp }.each do |name| const_set(name, Class.new(Unary)) diff --git a/activerecord/lib/arel/visitors/postgresql.rb b/activerecord/lib/arel/visitors/postgresql.rb index d4f21ff93e35325acabf0c4ad082cdb2935cd74c..65eceeef7424a10a39a2a95b397cb5e452b9f729 100644 --- a/activerecord/lib/arel/visitors/postgresql.rb +++ b/activerecord/lib/arel/visitors/postgresql.rb @@ -82,6 +82,16 @@ def visit_Arel_Nodes_IsDistinctFrom(o, collector) visit o.right, collector end + def visit_Arel_Nodes_NullsFirst(o, collector) + visit o.expr, collector + collector << " NULLS FIRST" + end + + def visit_Arel_Nodes_NullsLast(o, collector) + visit o.expr, collector + collector << " NULLS LAST" + end + # Used by Lateral visitor to enclose select queries in parentheses def grouping_parentheses(o, collector) if o.expr.is_a? Nodes::SelectStatement diff --git a/activerecord/test/cases/arel/visitors/postgres_test.rb b/activerecord/test/cases/arel/visitors/postgres_test.rb index 34709ae7eae2de9607f0cc9bcabc0fef5ebb1c91..e0096f52cb5da50be83b866775856bd5b83b8a85 100644 --- a/activerecord/test/cases/arel/visitors/postgres_test.rb +++ b/activerecord/test/cases/arel/visitors/postgres_test.rb @@ -315,6 +315,22 @@ def compile(node) _(sql).must_be_like %{ "users"."name" IS DISTINCT FROM NULL } end end + + describe "Nodes::Ordering" do + it "should handle nulls first" do + test = Table.new(:users)[:first_name].desc.nulls_first + _(compile(test)).must_be_like %{ + "users"."first_name" DESC NULLS FIRST + } + end + + it "should handle nulls last" do + test = Table.new(:users)[:first_name].desc.nulls_last + _(compile(test)).must_be_like %{ + "users"."first_name" DESC NULLS LAST + } + end + end end end end