class NoSE::Serialize::QueryPlanBuilder

Reconstruct the steps of a query plan

Public Instance Methods

call(_, represented:, fragment:, **) click to toggle source
# File lib/nose/serialize.rb, line 557
def call(_, represented:, fragment:, **)
  workload = represented.workload

  if fragment['query'].nil?
    query = OpenStruct.new group: fragment['group']
    state = nil
  else
    query = Statement.parse fragment['query'], workload.model,
                            group: fragment['group']
    state = Plans::QueryState.new query, workload
  end

  plan = build_plan query, represented.cost_model, fragment
  add_plan_steps plan, workload, fragment['steps'], represented.indexes,
                 state

  plan
end

Private Instance Methods

add_index_lookup_filters(step, step_hash, f) click to toggle source

Add filters to a constructed index lookup step @return [void]

# File lib/nose/serialize.rb, line 653
def add_index_lookup_filters(step, step_hash, f)
  eq_filter = (step_hash['eq_filter'] || []).map(&f)
  step.instance_variable_set(:@eq_filter, eq_filter)

  range_filter = step_hash['range_filter']
  range_filter = f.call(range_filter) unless range_filter.nil?
  step.instance_variable_set(:@range_filter, range_filter)
end
add_plan_steps(plan, workload, steps_fragment, indexes, state) click to toggle source

Loop over all steps in the plan and reconstruct them @return [void]

# File lib/nose/serialize.rb, line 592
def add_plan_steps(plan, workload, steps_fragment, indexes, state)
  parent = Plans::RootPlanStep.new state
  f = ->(field) { workload.model[field['parent']][field['name']] }

  steps_fragment.each do |step_hash|
    step = build_step step_hash, state, parent, indexes, f
    rebuild_step_state step, step_hash
    plan << step
    parent = step
  end
end
build_filter_step(step_hash, _state, parent, _indexes, f) click to toggle source

Rebuild a filter step @return [Plans::FilterPlanStep]

# File lib/nose/serialize.rb, line 628
def build_filter_step(step_hash, _state, parent, _indexes, f)
  eq = step_hash['eq'].map(&f)
  range = f.call(step_hash['range']) if step_hash['range']
  Plans::FilterPlanStep.new eq, range, parent.state
end
build_index_lookup_step(step_hash, state, parent, indexes, f) click to toggle source

Rebuild an index lookup step @return [Plans::IndexLookupPlanStep]

# File lib/nose/serialize.rb, line 636
def build_index_lookup_step(step_hash, state, parent, indexes, f)
  index_key = step_hash['index']['key']
  step_index = indexes.find { |i| i.key == index_key }
  step = Plans::IndexLookupPlanStep.new step_index, state, parent
  add_index_lookup_filters step, step_hash, f

  order_by = (step_hash['order_by'] || []).map(&f)
  step.instance_variable_set(:@order_by, order_by)

  limit = step_hash['limit']
  step.instance_variable_set(:@limit, limit.to_i) unless limit.nil?

  step
end
build_limit_step(step_hash, _state, parent, _indexes, _f) click to toggle source

Rebuild a limit step @return [Plans::LimitPlanStep]

# File lib/nose/serialize.rb, line 614
def build_limit_step(step_hash, _state, parent, _indexes, _f)
  limit = step_hash['limit'].to_i
  Plans::LimitPlanStep.new limit, parent.state
end
build_plan(query, cost_model, fragment) click to toggle source

Build a new query plan @return [Plans::QueryPlan]

# File lib/nose/serialize.rb, line 580
def build_plan(query, cost_model, fragment)
  plan = Plans::QueryPlan.new query, cost_model

  plan.instance_variable_set(:@name, fragment['name']) \
    unless fragment['name'].nil?
  plan.instance_variable_set(:@weight, fragment['weight'])

  plan
end
build_sort_step(step_hash, _state, parent, _indexes, f) click to toggle source

Rebuild a sort step @return [Plans::SortPlanStep]

# File lib/nose/serialize.rb, line 621
def build_sort_step(step_hash, _state, parent, _indexes, f)
  sort_fields = step_hash['sort_fields'].map(&f)
  Plans::SortPlanStep.new sort_fields, parent.state
end
build_step(step_hash, state, parent, indexes, f) click to toggle source

Rebuild a step from a hash using the given set of indexes The final parameter is a function which maps field names to instances @return [Plans::PlanStep]

# File lib/nose/serialize.rb, line 607
def build_step(step_hash, state, parent, indexes, f)
  send "build_#{step_hash['type']}_step".to_sym,
       step_hash, state, parent, indexes, f
end
rebuild_step_state(step, step_hash) click to toggle source

Rebuild the state of the step from the provided hash @return [void]

# File lib/nose/serialize.rb, line 664
def rebuild_step_state(step, step_hash)
  return if step.state.nil?

  # Copy the correct cardinality
  # XXX This may not preserve all the necessary state
  state = step.state.dup
  state.instance_variable_set :@cardinality, step_hash['cardinality']
  step.instance_variable_set :@cost, step_hash['cost']
  step.state = state.freeze
end