Class: AwesomeExplain::SqlPlanTree

Inherits:
Object
  • Object
show all
Defined in:
app/models/awesome_explain/sql_plan_tree.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeSqlPlanTree

Returns a new instance of SqlPlanTree.



19
20
21
22
23
24
25
26
27
28
29
30
# File 'app/models/awesome_explain/sql_plan_tree.rb', line 19

def initialize
  @startup_cost = 0
  @total_cost = 0
  @rows = 0
  @width = 0
  @actual_startup_time = 0
  @actual_total_time = 0
  @actual_rows = 0
  @actual_loops = 0
  @seq_scans = 0
  @plan_stats = ::AwesomeExplain::SqlPlanStats.new
end

Instance Attribute Details

#actual_loopsObject

Returns the value of attribute actual_loops.



2
3
4
# File 'app/models/awesome_explain/sql_plan_tree.rb', line 2

def actual_loops
  @actual_loops
end

#actual_rowsObject

Returns the value of attribute actual_rows.



2
3
4
# File 'app/models/awesome_explain/sql_plan_tree.rb', line 2

def actual_rows
  @actual_rows
end

#actual_startup_timeObject

Returns the value of attribute actual_startup_time.



2
3
4
# File 'app/models/awesome_explain/sql_plan_tree.rb', line 2

def actual_startup_time
  @actual_startup_time
end

#actual_total_timeObject

Returns the value of attribute actual_total_time.



2
3
4
# File 'app/models/awesome_explain/sql_plan_tree.rb', line 2

def actual_total_time
  @actual_total_time
end

#idsObject

Returns the value of attribute ids.



2
3
4
# File 'app/models/awesome_explain/sql_plan_tree.rb', line 2

def ids
  @ids
end

#plan_statsObject

Returns the value of attribute plan_stats.



2
3
4
# File 'app/models/awesome_explain/sql_plan_tree.rb', line 2

def plan_stats
  @plan_stats
end

#plans_countObject

Returns the value of attribute plans_count.



2
3
4
# File 'app/models/awesome_explain/sql_plan_tree.rb', line 2

def plans_count
  @plans_count
end

#rootObject

Returns the value of attribute root.



2
3
4
# File 'app/models/awesome_explain/sql_plan_tree.rb', line 2

def root
  @root
end

#rowsObject

Returns the value of attribute rows.



2
3
4
# File 'app/models/awesome_explain/sql_plan_tree.rb', line 2

def rows
  @rows
end

#seq_scanObject Also known as: seq_scan?

Returns the value of attribute seq_scan.



2
3
4
# File 'app/models/awesome_explain/sql_plan_tree.rb', line 2

def seq_scan
  @seq_scan
end

#seq_scansObject

Returns the value of attribute seq_scans.



2
3
4
# File 'app/models/awesome_explain/sql_plan_tree.rb', line 2

def seq_scans
  @seq_scans
end

#startup_costObject

Returns the value of attribute startup_cost.



2
3
4
# File 'app/models/awesome_explain/sql_plan_tree.rb', line 2

def startup_cost
  @startup_cost
end

#total_costObject

Returns the value of attribute total_cost.



2
3
4
# File 'app/models/awesome_explain/sql_plan_tree.rb', line 2

def total_cost
  @total_cost
end

#widthObject

Returns the value of attribute width.



2
3
4
# File 'app/models/awesome_explain/sql_plan_tree.rb', line 2

def width
  @width
end

Class Method Details

.build(plan) ⇒ Object



32
33
34
35
36
37
38
39
40
41
42
# File 'app/models/awesome_explain/sql_plan_tree.rb', line 32

def self.build(plan)
  tree = self.new
  tree.ids = (2..500).to_a # Ugh!!!
  root = ::AwesomeExplain::SqlPlanNode.build(plan.first.dig('Plan'))
  tree.root = root
  tree.update_tree_stats(root)
  root.id = 1
  tree.plans_count = 1
  build_recursive(plan.first.dig('Plan', 'Plans'), root, tree)
  tree
end

.build_recursive(data, parent, tree) ⇒ Object



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'app/models/awesome_explain/sql_plan_tree.rb', line 44

def self.build_recursive(data, parent, tree)
  return unless data.present?

  if data.is_a?(Array)
    data.each do |plan|
      build_recursive(plan, parent, tree)
    end
  elsif data.is_a?(Hash) && data.dig('Plans').present?
    node = ::AwesomeExplain::SqlPlanNode.build(data, parent)
    node.id = tree.ids.shift
    parent.children << node
    tree.plans_count += 1
    tree.seq_scans += 1 if node.seq_scan?
    tree.update_tree_stats(node)
    build_recursive(data.dig('Plans'), node, tree)
  elsif data.is_a?(Hash) && data.dig('Plans').nil?
    node = ::AwesomeExplain::SqlPlanNode.build(data, parent)
    tree.update_tree_stats(node)
    node.id = tree.ids.shift
    tree.plans_count += 1
    tree.seq_scans += 1 if node.seq_scan?
    parent.children << node
  end
end

Instance Method Details

#treevizObject



69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'app/models/awesome_explain/sql_plan_tree.rb', line 69

def treeviz
  return unless root.present?
  output = []
  queue = [root]
  while(!queue.empty?) do
    node = queue.shift
    output << node.treeviz
    node.children.each do |child|
      queue << child
    end
  end

  output
end

#update_tree_stats(node) ⇒ Object



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
# File 'app/models/awesome_explain/sql_plan_tree.rb', line 84

def update_tree_stats(node)
  self.startup_cost += node.startup_cost
  self.total_cost += node.total_cost
  self.rows += node.rows
  self.width += node.width
  self.actual_startup_time += node.actual_startup_time
  self.actual_total_time += node.actual_total_time
  self.actual_rows += node.actual_rows
  self.actual_loops += node.actual_loops

  # Plan Stats
  plan_stats.total_rows_planned += node.rows
  plan_stats.total_rows += node.actual_rows
  plan_stats.total_loops += node.actual_loops
  plan_stats.seq_scans += 1 if node.seq_scan?

  relation_name = node.relation_name
  if relation_name
    if plan_stats.table_stats.dig(relation_name).nil?
      plan_stats.table_stats[relation_name] = {
        count: 0,
        time: 0
      }
    end
    plan_stats.table_stats[relation_name][:count] += 1
    plan_stats.table_stats[relation_name][:time] += node.actual_total_time
  end


  node_type = node.type
  if node_type
    if plan_stats.node_type_stats.dig(node_type).nil?
      plan_stats.node_type_stats[node_type] = {
        count: 0
      }
    end
    plan_stats.node_type_stats[node_type][:count] += 1
  end

  index_name = node.index_name
  if index_name
    if plan_stats.index_stats.dig(index_name).nil?
      plan_stats.index_stats[index_name] = {
        count: 0
      }
      plan_stats.index_stats[index_name][:count] += 1
    end
  end
end