Class: PgInsights::QueryExecution

Inherits:
ApplicationRecord show all
Defined in:
app/models/pg_insights/query_execution.rb

Constant Summary collapse

EXECUTION_TYPES =
%w[execute analyze both].freeze
STATUSES =
%w[pending running completed failed].freeze

Instance Method Summary collapse

Instance Method Details

#completed?Boolean

Returns:

  • (Boolean)


33
34
35
# File 'app/models/pg_insights/query_execution.rb', line 33

def completed?
  status == "completed"
end

#display_summaryObject



151
152
153
154
155
156
157
# File 'app/models/pg_insights/query_execution.rb', line 151

def display_summary
  parts = []
  parts << "#{formatted_total_time}" if total_time_ms.present?
  parts << "Cost: #{formatted_query_cost}" if query_cost.present?
  parts << "#{result_rows_count} rows" if result_rows_count.present?
  parts.join("")
end

#display_titleObject

History display helpers (public methods)



146
147
148
149
# File 'app/models/pg_insights/query_execution.rb', line 146

def display_title
  return sql_text.truncate(50) if sql_text.present?
  "Query ##{id}"
end

#failed?Boolean

Returns:

  • (Boolean)


37
38
39
# File 'app/models/pg_insights/query_execution.rb', line 37

def failed?
  status == "failed"
end

#formatted_query_costObject



69
70
71
72
73
74
75
76
77
# File 'app/models/pg_insights/query_execution.rb', line 69

def formatted_query_cost
  return nil unless query_cost

  if query_cost < 1000
    query_cost.round(2)
  else
    "#{(query_cost / 1000).round(1)}K"
  end
end

#formatted_total_timeObject



59
60
61
62
63
64
65
66
67
# File 'app/models/pg_insights/query_execution.rb', line 59

def formatted_total_time
  return nil unless total_time_ms

  if total_time_ms < 1000
    "#{total_time_ms.round(2)}ms"
  else
    "#{(total_time_ms / 1000).round(2)}s"
  end
end

#has_performance_issues?Boolean

Returns:

  • (Boolean)


98
99
100
101
102
103
104
105
# File 'app/models/pg_insights/query_execution.rb', line 98

def has_performance_issues?
  return false unless performance_insights.present?

  insights = performance_insights
  insights["issues_detected"] == true ||
  insights["slow_operations"].present? ||
  insights["missing_indexes"].present?
end

#has_plan_data?Boolean

Plan analysis helpers

Returns:

  • (Boolean)


80
81
82
# File 'app/models/pg_insights/query_execution.rb', line 80

def has_plan_data?
  execution_plan.present?
end

#has_result_data?Boolean

Result data helpers

Returns:

  • (Boolean)


108
109
110
# File 'app/models/pg_insights/query_execution.rb', line 108

def has_result_data?
  result_data.present? && result_rows_count.present?
end

#has_timing_data?Boolean

Performance metrics

Returns:

  • (Boolean)


55
56
57
# File 'app/models/pg_insights/query_execution.rb', line 55

def has_timing_data?
  planning_time_ms.present? || execution_time_ms.present?
end

#includes_analysis?Boolean

Returns:

  • (Boolean)


50
51
52
# File 'app/models/pg_insights/query_execution.rb', line 50

def includes_analysis?
  execution_type.in?([ "analyze", "both" ])
end

#includes_execution?Boolean

Execution type checks

Returns:

  • (Boolean)


46
47
48
# File 'app/models/pg_insights/query_execution.rb', line 46

def includes_execution?
  execution_type.in?([ "execute", "both" ])
end

#mark_as_completed!(results = {}) ⇒ Object



126
127
128
129
130
131
132
133
# File 'app/models/pg_insights/query_execution.rb', line 126

def mark_as_completed!(results = {})
  update!(
    status: "completed",
    completed_at: Time.current,
    duration_ms: calculate_duration,
    **results
  )
end

#mark_as_failed!(error_msg, error_detail = nil) ⇒ Object



135
136
137
138
139
140
141
142
143
# File 'app/models/pg_insights/query_execution.rb', line 135

def mark_as_failed!(error_msg, error_detail = nil)
  update!(
    status: "failed",
    completed_at: Time.current,
    duration_ms: calculate_duration,
    error_message: error_msg,
    error_detail: error_detail
  )
end

#mark_as_running!Object

Status transitions



119
120
121
122
123
124
# File 'app/models/pg_insights/query_execution.rb', line 119

def mark_as_running!
  update!(
    status: "running",
    started_at: Time.current
  )
end

#optimization_suggestionsObject



92
93
94
95
96
# File 'app/models/pg_insights/query_execution.rb', line 92

def optimization_suggestions
  return [] unless performance_insights.present?

  performance_insights["suggestions"] || []
end

#pending?Boolean

Status management

Returns:

  • (Boolean)


25
26
27
# File 'app/models/pg_insights/query_execution.rb', line 25

def pending?
  status == "pending"
end

#performance_classObject



159
160
161
162
163
164
165
# File 'app/models/pg_insights/query_execution.rb', line 159

def performance_class
  return "performance-excellent" if total_time_ms && total_time_ms < 50
  return "performance-good" if total_time_ms && total_time_ms < 200
  return "performance-fair" if total_time_ms && total_time_ms < 1000
  return "performance-poor" if total_time_ms && total_time_ms >= 1000
  "performance-unknown"
end

#plan_nodesObject



84
85
86
87
88
89
90
# File 'app/models/pg_insights/query_execution.rb', line 84

def plan_nodes
  return [] unless execution_plan.present?

  # Extract plan nodes from PostgreSQL EXPLAIN output
  plan_data = execution_plan.is_a?(Array) ? execution_plan.first : execution_plan
  extract_plan_nodes(plan_data["Plan"]) if plan_data && plan_data["Plan"]
end

#result_summaryObject



112
113
114
115
116
# File 'app/models/pg_insights/query_execution.rb', line 112

def result_summary
  return nil unless has_result_data?

  "#{result_rows_count} #{'row'.pluralize(result_rows_count)}#{result_columns_count} #{'column'.pluralize(result_columns_count)}"
end

#running?Boolean

Returns:

  • (Boolean)


29
30
31
# File 'app/models/pg_insights/query_execution.rb', line 29

def running?
  status == "running"
end

#success?Boolean

Returns:

  • (Boolean)


41
42
43
# File 'app/models/pg_insights/query_execution.rb', line 41

def success?
  completed? && error_message.blank?
end