Module: Switchman::ActiveRecord::QueryMethods

Defined in:
lib/switchman/active_record/query_methods.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#shard_source_valueObject

shard_value is one of:

A shard
An array or relation of shards
An AR object (query runs against that object's associated_shards)

shard_source_value is one of:

:implicit - inferred from current shard when relation was created, or primary key where clause
:explicit - explicit set on the relation
:to_a - a special value that Relation#to_a uses when querying multiple shards to
        remove primary keys from conditions that aren't applicable to the current shard


13
14
15
# File 'lib/switchman/active_record/query_methods.rb', line 13

def shard_source_value
  @shard_source_value
end

#shard_valueObject

shard_value is one of:

A shard
An array or relation of shards
An AR object (query runs against that object's associated_shards)

shard_source_value is one of:

:implicit - inferred from current shard when relation was created, or primary key where clause
:explicit - explicit set on the relation
:to_a - a special value that Relation#to_a uses when querying multiple shards to
        remove primary keys from conditions that aren't applicable to the current shard


13
14
15
# File 'lib/switchman/active_record/query_methods.rb', line 13

def shard_value
  @shard_value
end

Instance Method Details

#build_where(opts, other = []) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/switchman/active_record/query_methods.rb', line 45

def build_where(opts, other = [])
  source_shard = Shard.current(klass.shard_category)
  case opts
  when Hash, Arel::Nodes::Node
    predicates = super
    infer_shards_from_primary_key(predicates) if shard_source_value == :implicit && shard_value.is_a?(Shard)
    predicates = transpose_predicates(predicates, source_shard, primary_shard) if shard_source_value != :explicit
    predicates
  else
    super
  end
end

#having(opts, *rest) ⇒ Object



37
38
39
40
41
42
43
# File 'lib/switchman/active_record/query_methods.rb', line 37

def having(opts, *rest)
  return self if opts.blank?

  relation = clone
  relation.having_values += relation.build_where(opts, rest)
  relation
end

#primary_shardObject

the shard that where_values are relative to. if it’s multiple shards, they’re stored relative to the first shard



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/switchman/active_record/query_methods.rb', line 60

def primary_shard
  case shard_value
  when Shard, DefaultShard
    shard_value
  # associated_shards
  when ::ActiveRecord::Base
    shard_value.shard
  when Array
    shard_value.first
  when ::ActiveRecord::Relation
    Shard.default
  else
    raise ArgumentError("invalid shard value #{shard_value}")
  end
end

#shard(value, source = :explicit) ⇒ Object



15
16
17
18
19
20
21
22
23
24
# File 'lib/switchman/active_record/query_methods.rb', line 15

def shard(value, source = :explicit)
  relation = clone
  relation.shard_value = value
  relation.shard_source_value = source
  if (primary_shard != relation.primary_shard || source == :to_a)
    relation.where_values = relation.transpose_predicates(relation.where_values, primary_shard, relation.primary_shard, source == :to_a) if !relation.where_values.empty?
    relation.having_values = relation.transpose_predicates(relation.having_values, primary_shard, relation.primary_shard, source == :to_a) if !relation.having_values.empty?
  end
  relation
end

#transpose_predicates(predicates, source_shard, target_shard, remove_nonlocal_primary_keys = false) ⇒ Object



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
# File 'lib/switchman/active_record/query_methods.rb', line 146

def transpose_predicates(predicates, source_shard, target_shard, remove_nonlocal_primary_keys = false)
  predicates.map do |predicate|
    next predicate unless predicate.is_a?(Arel::Nodes::Binary) && type = transposable_attribute_type(predicate.left)

    remove = true if type == :primary && remove_nonlocal_primary_keys && predicate.left.relation.engine == klass

    new_right_value = case predicate.right
    when Array
      local_ids = []
      predicate.right.each do |value|
        local_id = Shard.relative_id_for(value, source_shard, target_shard)
        local_ids << local_id unless remove && local_id > Shard::IDS_PER_SHARD
      end
      local_ids
    when Arel::Nodes::BindParam
      # look for a bind param with a matching column name
      if @bind_params && idx = @bind_params.find_index{|b| b.is_a?(Array) && b.first.try(:name) == predicate.left}
        column, value = @bind_params[idx]
        local_id = Shard.relative_id_for(value, source_shard, target_shard)
        local_id = [] if remove && local_id > Shard::IDS_PER_SHARD
        @bind_params[idx] = [column, local_id]
      end
      predicate.right
    else
      local_id = Shard.relative_id_for(predicate.right, source_shard, target_shard)
      local_id = [] if remove && local_id > Shard::IDS_PER_SHARD
      local_id
    end

    if new_right_value == predicate.right
      predicate
    else
      predicate.class.new(predicate.left, new_right_value)
    end
  end
end

#where(opts, *rest) ⇒ Object

replace these with versions that call build_where on the result relation, not the source relation (so build_where is able to implicitly change the shard_value)



29
30
31
32
33
34
35
# File 'lib/switchman/active_record/query_methods.rb', line 29

def where(opts, *rest)
  return self if opts.blank?

  relation = clone
  relation.where_values += relation.build_where(opts, rest)
  relation
end