Merge branch 'fix-array-builder-wheres'
This commit is contained in:
commit
70ddb8a704
@ -7,15 +7,21 @@ module Numeric
|
||||
def serialize(value)
|
||||
cast(value)
|
||||
end
|
||||
alias :unchecked_serialize :serialize
|
||||
|
||||
def cast(value)
|
||||
value = \
|
||||
# Checks whether the value is numeric. Spaceship operator
|
||||
# will return nil if value is not numeric.
|
||||
value = if value <=> 0
|
||||
value
|
||||
else
|
||||
case value
|
||||
when true then 1
|
||||
when false then 0
|
||||
when ::String then value.presence
|
||||
else value
|
||||
else value.presence
|
||||
end
|
||||
end
|
||||
|
||||
super(value)
|
||||
end
|
||||
|
||||
|
@ -28,15 +28,24 @@ def serialize(value)
|
||||
ensure_in_range(super)
|
||||
end
|
||||
|
||||
def serializable?(value)
|
||||
cast_value = cast(value)
|
||||
in_range?(cast_value) && super
|
||||
end
|
||||
|
||||
private
|
||||
attr_reader :range
|
||||
|
||||
def in_range?(value)
|
||||
!value || range.member?(value)
|
||||
end
|
||||
|
||||
def cast_value(value)
|
||||
value.to_i rescue nil
|
||||
end
|
||||
|
||||
def ensure_in_range(value)
|
||||
if value && !range.cover?(value)
|
||||
unless in_range?(value)
|
||||
raise ActiveModel::RangeError, "#{value} is out of range for #{self.class} with limit #{_limit} bytes"
|
||||
end
|
||||
value
|
||||
|
@ -11,6 +11,14 @@ def initialize(precision: nil, limit: nil, scale: nil)
|
||||
@limit = limit
|
||||
end
|
||||
|
||||
# Returns true if this type can convert +value+ to a type that is usable
|
||||
# by the database. For example a boolean type can return +true+ if the
|
||||
# value parameter is a Ruby boolean, but may return +false+ if the value
|
||||
# parameter is some other object.
|
||||
def serializable?(value)
|
||||
true
|
||||
end
|
||||
|
||||
def type # :nodoc:
|
||||
end
|
||||
|
||||
@ -45,6 +53,7 @@ def cast(value)
|
||||
def serialize(value)
|
||||
value
|
||||
end
|
||||
alias :unchecked_serialize :serialize
|
||||
|
||||
# Type casts a value for schema dumping. This method is private, as we are
|
||||
# hoping to remove it entirely.
|
||||
|
@ -139,12 +139,17 @@ def deserialize(value)
|
||||
mapping.key(subtype.deserialize(value))
|
||||
end
|
||||
|
||||
def serializable?(value)
|
||||
(value.blank? || mapping.has_key?(value) || mapping.has_value?(value)) && super
|
||||
end
|
||||
|
||||
def serialize(value)
|
||||
mapping.fetch(value, value)
|
||||
end
|
||||
alias :unchecked_serialize :serialize
|
||||
|
||||
def assert_valid_value(value)
|
||||
unless value.blank? || mapping.has_key?(value) || mapping.has_value?(value)
|
||||
unless serializable?(value)
|
||||
raise ArgumentError, "'#{value}' is not a valid #{name}"
|
||||
end
|
||||
end
|
||||
|
@ -21,10 +21,21 @@ def call(attribute, value)
|
||||
when 0 then NullPredicate
|
||||
when 1 then predicate_builder.build(attribute, values.first)
|
||||
else
|
||||
values.map! do |v|
|
||||
predicate_builder.build_bind_attribute(attribute.name, v)
|
||||
if nils.empty? && ranges.empty?
|
||||
type = attribute.type_caster
|
||||
|
||||
casted_values = values.map do |raw_value|
|
||||
type.unchecked_serialize(raw_value) if type.serializable?(raw_value)
|
||||
end
|
||||
|
||||
casted_values.compact!
|
||||
|
||||
return Arel::Nodes::HomogeneousIn.new(casted_values, attribute, :in)
|
||||
else
|
||||
attribute.in values.map { |v|
|
||||
predicate_builder.build_bind_attribute(attribute.name, v)
|
||||
}
|
||||
end
|
||||
values.empty? ? NullPredicate : attribute.in(values)
|
||||
end
|
||||
|
||||
unless nils.empty?
|
||||
|
@ -137,7 +137,7 @@ def predicates_unreferenced_by(other)
|
||||
end
|
||||
|
||||
def equality_node?(node)
|
||||
node.respond_to?(:operator) && node.operator == :==
|
||||
!node.is_a?(String) && node.equality?
|
||||
end
|
||||
|
||||
def invert_predicate(node)
|
||||
|
@ -9,10 +9,14 @@ def initialize(types)
|
||||
|
||||
def type_cast_for_database(attr_name, value)
|
||||
return value if value.is_a?(Arel::Nodes::BindParam)
|
||||
type = types.type_for_attribute(attr_name)
|
||||
type = type_for_attribute(attr_name)
|
||||
type.serialize(value)
|
||||
end
|
||||
|
||||
def type_for_attribute(name)
|
||||
types.type_for_attribute(name)
|
||||
end
|
||||
|
||||
private
|
||||
attr_reader :types
|
||||
end
|
||||
|
@ -9,6 +9,10 @@ class Attribute < Struct.new :relation, :name
|
||||
include Arel::OrderPredications
|
||||
include Arel::Math
|
||||
|
||||
def type_caster
|
||||
relation.type_for_attribute(name)
|
||||
end
|
||||
|
||||
###
|
||||
# Create a node for lowering this attribute
|
||||
def lower
|
||||
|
@ -18,6 +18,7 @@
|
||||
# unary
|
||||
require "arel/nodes/unary"
|
||||
require "arel/nodes/grouping"
|
||||
require "arel/nodes/homogeneous_in"
|
||||
require "arel/nodes/ordering"
|
||||
require "arel/nodes/ascending"
|
||||
require "arel/nodes/descending"
|
||||
|
@ -5,6 +5,8 @@ module Nodes
|
||||
class Equality < Arel::Nodes::Binary
|
||||
def operator; :== end
|
||||
|
||||
def equality?; true; end
|
||||
|
||||
def invert
|
||||
Arel::Nodes::NotEqual.new(left, right)
|
||||
end
|
||||
|
57
activerecord/lib/arel/nodes/homogeneous_in.rb
Normal file
57
activerecord/lib/arel/nodes/homogeneous_in.rb
Normal file
@ -0,0 +1,57 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Arel # :nodoc: all
|
||||
module Nodes
|
||||
class HomogeneousIn < Node
|
||||
attr_reader :attribute, :values, :type
|
||||
|
||||
def initialize(values, attribute, type)
|
||||
@values = values
|
||||
@attribute = attribute
|
||||
@type = type
|
||||
end
|
||||
|
||||
def hash
|
||||
ivars.hash
|
||||
end
|
||||
|
||||
def eql?(other)
|
||||
super || (self.class == other.class && self.ivars == other.ivars)
|
||||
end
|
||||
alias :== :eql?
|
||||
|
||||
def equality?
|
||||
true
|
||||
end
|
||||
|
||||
def invert
|
||||
Arel::Nodes::HomogeneousIn.new(values, attribute, type == :in ? :notin : :in)
|
||||
end
|
||||
|
||||
def left
|
||||
attribute
|
||||
end
|
||||
|
||||
def table_name
|
||||
attribute.relation.table_alias || attribute.relation.name
|
||||
end
|
||||
|
||||
def column_name
|
||||
attribute.name
|
||||
end
|
||||
|
||||
def fetch_attribute(&block)
|
||||
if attribute
|
||||
yield attribute
|
||||
else
|
||||
expr.fetch_attribute(&block)
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
def ivars
|
||||
[@attribute, @values, @type]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -44,6 +44,8 @@ def to_sql(engine = Table.engine)
|
||||
|
||||
def fetch_attribute
|
||||
end
|
||||
|
||||
def equality?; false; end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -19,6 +19,10 @@ def type_cast_for_database(*args)
|
||||
relation.type_cast_for_database(*args)
|
||||
end
|
||||
|
||||
def type_for_attribute(name)
|
||||
relation.type_for_attribute(name)
|
||||
end
|
||||
|
||||
def able_to_type_cast?
|
||||
relation.respond_to?(:able_to_type_cast?) && relation.able_to_type_cast?
|
||||
end
|
||||
|
@ -100,6 +100,10 @@ def type_cast_for_database(attribute_name, value)
|
||||
type_caster.type_cast_for_database(attribute_name, value)
|
||||
end
|
||||
|
||||
def type_for_attribute(name)
|
||||
type_caster.type_for_attribute(name)
|
||||
end
|
||||
|
||||
def able_to_type_cast?
|
||||
!type_caster.nil?
|
||||
end
|
||||
|
@ -161,6 +161,12 @@ def visit_Arel_Nodes_Casted(o)
|
||||
visit_edge o, "attribute"
|
||||
end
|
||||
|
||||
def visit_Arel_Nodes_HomogeneousIn(o)
|
||||
visit_edge o, "values"
|
||||
visit_edge o, "type"
|
||||
visit_edge o, "attribute"
|
||||
end
|
||||
|
||||
def visit_Arel_Attribute(o)
|
||||
visit_edge o, "relation"
|
||||
visit_edge o, "name"
|
||||
|
@ -321,6 +321,31 @@ def visit_Arel_Nodes_Grouping(o, collector)
|
||||
end
|
||||
end
|
||||
|
||||
def visit_Arel_Nodes_HomogeneousIn(o, collector)
|
||||
collector.preparable = false
|
||||
collector << "("
|
||||
|
||||
collector << quote_table_name(o.table_name) << "." << quote_column_name(o.column_name)
|
||||
|
||||
if o.type == :in
|
||||
collector << "IN ("
|
||||
else
|
||||
collector << "NOT IN ("
|
||||
end
|
||||
|
||||
values = o.values.map { |v| @connection.quote v }
|
||||
|
||||
expr = if values.empty?
|
||||
@connection.quote(nil)
|
||||
else
|
||||
values.join(",")
|
||||
end
|
||||
|
||||
collector << expr
|
||||
collector << "))"
|
||||
collector
|
||||
end
|
||||
|
||||
def visit_Arel_SelectManager(o, collector)
|
||||
collector << "("
|
||||
visit(o.ast, collector) << ")"
|
||||
|
Loading…
Reference in New Issue
Block a user