diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb index e46e47102b33f..2a651598925d0 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb @@ -7,7 +7,11 @@ module OID # :nodoc: class Array < Type::Value # :nodoc: include ActiveModel::Type::Helpers::Mutable - Data = Struct.new(:encoder, :values) # :nodoc: + Data = Struct.new(:encoder, :values) do # :nodoc: + def hash + [encoder.name, encoder.delimiter, values].hash + end + end attr_reader :subtype, :delimiter delegate :type, :user_input_in_time_zone, :limit, :precision, :scale, to: :subtype diff --git a/activerecord/lib/arel/visitors/postgresql.rb b/activerecord/lib/arel/visitors/postgresql.rb index 69aab80c8e1df..0762ea0d8aa73 100644 --- a/activerecord/lib/arel/visitors/postgresql.rb +++ b/activerecord/lib/arel/visitors/postgresql.rb @@ -78,6 +78,36 @@ 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 + + # Postgres-specific implementation that uses `col = any('{1,2}')` instead of `col IN (1,2)` + # to avoid pg_stat_statements churn + def visit_Arel_Nodes_HomogeneousIn(o, collector) + oid = ActiveRecord::ConnectionAdapters::PostgreSQL::OID + case o.attribute.type_caster + when oid::Bytea, oid::Jsonb + return super + end + + visit o.left, collector + collector << (o.type == :in ? " = any(" : " != all(") + + type_caster = oid::Array.new(o.attribute.type_caster, ",") + values = [type_caster.serialize(o.casted_values)] + proc_for_binds = -> value { ActiveModel::Attribute.with_cast_value(o.attribute.name, value, type_caster) } + collector.add_binds(values, proc_for_binds, &bind_block) + + collector << ")" + end + BIND_BLOCK = proc { |i| "$#{i}" } private_constant :BIND_BLOCK