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
|
# frozen_string_literal: true
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 merge(other)
WhereClause.new(
predicates_unreferenced_by(other) + 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
or_clause = WhereClause.new(
[left.ast.or(right.ast)],
)
common + or_clause
end
end
def to_h(table_name = nil)
equalities = equalities(predicates)
if table_name
equalities = equalities.select do |node|
node.left.relation.name == table_name
end
end
equalities.map { |node|
name = node.left.name.to_s
value = extract_node_value(node.right)
[name, value]
}.to_h
end
def ast
Arel::Nodes::And.new(predicates_with_wrapped_sql_literals)
end
def ==(other)
other.is_a?(WhereClause) &&
predicates == other.predicates
end
def invert(as = :nand)
if predicates.size == 1
inverted_predicates = [ invert_predicate(predicates.first) ]
elsif as == :nor
inverted_predicates = predicates.map { |node| invert_predicate(node) }
else
inverted_predicates = [ Arel::Nodes::Not.new(ast) ]
end
WhereClause.new(inverted_predicates)
end
def self.empty
@empty ||= new([])
end
protected
attr_reader :predicates
def referenced_columns
@referenced_columns ||= begin
equality_nodes = predicates.select { |n| equality_node?(n) }
Set.new(equality_nodes, &:left)
end
end
private
def equalities(predicates)
equalities = []
predicates.each do |node|
case node
when Arel::Nodes::Equality
equalities << node
when Arel::Nodes::And
equalities.concat equalities(node.children)
end
end
equalities
end
def predicates_unreferenced_by(other)
predicates.reject do |n|
equality_node?(n) && other.referenced_columns.include?(n.left)
end
end
def equality_node?(node)
node.respond_to?(:operator) && node.operator == :==
end
def invert_predicate(node)
case node
when NilClass
raise ArgumentError, "Invalid argument for .where.not(), got nil."
when Arel::Nodes::In
Arel::Nodes::NotIn.new(node.left, node.right)
when Arel::Nodes::IsNotDistinctFrom
Arel::Nodes::IsDistinctFrom.new(node.left, node.right)
when Arel::Nodes::IsDistinctFrom
Arel::Nodes::IsNotDistinctFrom.new(node.left, node.right)
when Arel::Nodes::Equality
Arel::Nodes::NotEqual.new(node.left, node.right)
when String
Arel::Nodes::Not.new(Arel::Nodes::SqlLiteral.new(node))
else
Arel::Nodes::Not.new(node)
end
end
def except_predicates(columns)
predicates.reject do |node|
Arel.fetch_attribute(node) { |attr| columns.include?(attr.name.to_s) }
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)
case node
when Array
node.map { |v| extract_node_value(v) }
when Arel::Nodes::Casted, Arel::Nodes::Quoted
node.val
when Arel::Nodes::BindParam
value = node.value
if value.respond_to?(:value_before_type_cast)
value.value_before_type_cast
else
value
end
end
end
end
end
end
|