From 66b19b5dec78a74e4280d69ac0e4801699ca691a Mon Sep 17 00:00:00 2001 From: Kevin Deisz Date: Tue, 31 Dec 2019 15:59:59 -0500 Subject: [PATCH] Allow #nulls_first and #nulls_last in PostgreSQL When using PostgreSQL, it's useful to be able to specify NULLS FIRST and NULLS LAST on ordered columns. With this commit you can do that now, as in: ```ruby User.arel_table[:first_name].desc.nulls_last ``` --- activerecord/lib/arel/nodes.rb | 1 + activerecord/lib/arel/nodes/ordering.rb | 18 ++++++++++++++++++ activerecord/lib/arel/nodes/unary.rb | 1 - activerecord/lib/arel/visitors/postgresql.rb | 10 ++++++++++ .../test/cases/arel/visitors/postgres_test.rb | 16 ++++++++++++++++ 5 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 activerecord/lib/arel/nodes/ordering.rb diff --git a/activerecord/lib/arel/nodes.rb b/activerecord/lib/arel/nodes.rb index f994754620..e7c1acc30a 100644 --- a/activerecord/lib/arel/nodes.rb +++ b/activerecord/lib/arel/nodes.rb @@ -18,6 +18,7 @@ require "arel/nodes/false" # 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 0000000000..4c4cb58c9f --- /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 6d1ac36b0e..cf1eca2831 100644 --- a/activerecord/lib/arel/nodes/unary.rb +++ b/activerecord/lib/arel/nodes/unary.rb @@ -36,7 +36,6 @@ module Arel # :nodoc: all 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 d4f21ff93e..65eceeef74 100644 --- a/activerecord/lib/arel/visitors/postgresql.rb +++ b/activerecord/lib/arel/visitors/postgresql.rb @@ -82,6 +82,16 @@ module Arel # :nodoc: all 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 34709ae7ea..e0096f52cb 100644 --- a/activerecord/test/cases/arel/visitors/postgres_test.rb +++ b/activerecord/test/cases/arel/visitors/postgres_test.rb @@ -315,6 +315,22 @@ module Arel _(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