1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
|
# frozen_string_literal: true
require "active_support/core_ext/array/extract"
module ActiveRecord
class Relation
class WhereClause # :nodoc:
delegate :any?, :empty?, to: :predicates
def initialize(predicates)
@predicates = predicates
end
def +(other)
WhereClause.new(predicates + other.predicates)
end
def -(other)
WhereClause.new(predicates - other.predicates)
end
def |(other)
WhereClause.new(predicates | other.predicates)
end
def merge(other)
predicates = except_predicates(other.extract_attributes)
WhereClause.new(predicates | other.predicates)
end
def except(*columns)
WhereClause.new(except_predicates(columns))
end
def or(other)
left = self - other
common = self - left
right = other - common
if left.empty? || right.empty?
common
else
left = left.ast
left = left.expr if left.is_a?(Arel::Nodes::Grouping)
right = right.ast
right = right.expr if right.is_a?(Arel::Nodes::Grouping)
or_clause = if left.is_a?(Arel::Nodes::Or)
Arel::Nodes::Or.new(left.children + [right])
else
Arel::Nodes::Or.new([left, right])
end
common.predicates << Arel::Nodes::Grouping.new(or_clause)
common
end
end
def to_h(table_name = nil, equality_only: false)
equalities(predicates, equality_only).each_with_object({}) do |node, hash|
next if table_name&.!= node.left.relation.name
name = node.left.name.to_s
value = extract_node_value(node.right)
hash[name] = value
end
end
def ast
predicates = predicates_with_wrapped_sql_literals
predicates.one? ? predicates.first : Arel::Nodes::And.new(predicates)
end
def ==(other)
other.is_a?(WhereClause) &&
predicates == other.predicates
end
alias :eql? :==
def hash
[self.class, predicates].hash
end
def invert
if predicates.size == 1
inverted_predicates = [ invert_predicate(predicates.first) ]
else
inverted_predicates = [ Arel::Nodes::Not.new(ast) ]
end
WhereClause.new(inverted_predicates)
end
def self.empty
@empty ||= new([]).freeze
end
def contradiction?
predicates.any? do |x|
case x
when Arel::Nodes::In
Array === x.right && x.right.empty?
when Arel::Nodes::Equality
x.right.respond_to?(:unboundable?) && x.right.unboundable?
end
end
end
def extract_attributes
attrs = []
each_attributes { |attr, _| attrs << attr }
attrs
end
protected
attr_reader :predicates
def referenced_columns
hash = {}
each_attributes { |attr, node| hash[attr] = node }
hash
end
private
def each_attributes
predicates.each do |node|
attr = extract_attribute(node) || begin
node.left if equality_node?(node) && node.left.is_a?(Arel::Predications)
end
yield attr, node if attr
end
end
def extract_attribute(node)
attr_node = nil
Arel.fetch_attribute(node) do |attr|
return if attr_node&.!= attr # all attr nodes should be the same
attr_node = attr
end
attr_node
end
def equalities(predicates, equality_only)
equalities = []
predicates.each do |node|
if equality_only ? Arel::Nodes::Equality === node : equality_node?(node)
equalities << node
elsif node.is_a?(Arel::Nodes::And)
equalities.concat equalities(node.children, equality_only)
end
end
equalities
end
def equality_node?(node)
!node.is_a?(String) && node.equality?
end
def invert_predicate(node)
case node
when NilClass
raise ArgumentError, "Invalid argument for .where.not(), got nil."
when String
Arel::Nodes::Not.new(Arel::Nodes::SqlLiteral.new(node))
else
node.invert
end
end
def except_predicates(columns)
attrs = columns.extract! { |node| node.is_a?(Arel::Attribute) }
non_attrs = columns.extract! { |node| node.is_a?(Arel::Predications) }
predicates.reject do |node|
if !non_attrs.empty? && node.equality? && node.left.is_a?(Arel::Predications)
non_attrs.include?(node.left)
end || Arel.fetch_attribute(node) do |attr|
attrs.include?(attr) || columns.include?(attr.name.to_s)
end
end
end
def predicates_with_wrapped_sql_literals
non_empty_predicates.map do |node|
case node
when Arel::Nodes::SqlLiteral, ::String
wrap_sql_literal(node)
else node
end
end
end
ARRAY_WITH_EMPTY_STRING = [""]
def non_empty_predicates
predicates - ARRAY_WITH_EMPTY_STRING
end
def wrap_sql_literal(node)
if ::String === node
node = Arel.sql(node)
end
Arel::Nodes::Grouping.new(node)
end
def extract_node_value(node)
if node.respond_to?(:value_before_type_cast)
node.value_before_type_cast
elsif Array === node
node.map { |v| extract_node_value(v) }
end
end
end
end
end
|