Class: PgInsights::InsightsController

Inherits:
ApplicationController show all
Defined in:
app/controllers/pg_insights/insights_controller.rb

Constant Summary collapse

MAX_ROWS =
1_000

Instance Method Summary collapse

Instance Method Details

#analyzeObject

POST /pg_insights/analyze



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
# File 'app/controllers/pg_insights/insights_controller.rb', line 56

def analyze
  sql = params.require(:sql)
  execution_type = params.fetch(:execution_type, "analyze")
  async = params.fetch(:async, false)

  unless read_only?(sql)
    render json: { error: "Only single SELECT statements are allowed" }, status: :unprocessable_entity
    return
  end

  begin
    if async.to_s == "true"
      execution = QueryAnalysisService.analyze_query_async(sql, execution_type: execution_type)
      render json: {
        execution_id: execution.id,
        status: execution.status,
        message: "Analysis started"
      }
    else
      execution = QueryAnalysisService.execute_query(sql, execution_type: execution_type)
      render json: format_execution_response(execution)
    end
  rescue => e
    Rails.logger.error "Analysis failed: #{e.message}"
    render json: { error: e.message }, status: :internal_server_error
  end
end

#compareObject



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
# File 'app/controllers/pg_insights/insights_controller.rb', line 125

def compare
  # Handle nested parameters from form submission
  execution_ids = params[:execution_ids] || params.dig(:insight, :execution_ids)

  if execution_ids.blank? || execution_ids.size != 2
    error_response = { error: "Please select exactly 2 queries to compare" }
    respond_to do |format|
      format.json { render json: error_response, status: :bad_request }
      format.html { redirect_to root_path, alert: error_response[:error] }
    end
    return
  end

  begin
            @execution_a = QueryExecution.find(execution_ids[0])
    @execution_b = QueryExecution.find(execution_ids[1])

    # Performance logging
    Rails.logger.info "PgInsights: Comparing query executions #{@execution_a.id} vs #{@execution_b.id}"

    start_time = Time.current
    @comparison_data = generate_comparison_data(@execution_a, @execution_b)
    comparison_duration = ((Time.current - start_time) * 1000).round(2)

    Rails.logger.info "PgInsights: Comparison completed in #{comparison_duration}ms"

    respond_to do |format|
      format.json { render json: @comparison_data }
      format.html { redirect_to root_path, notice: "Comparison completed" }
    end
  rescue ActiveRecord::RecordNotFound => e
    error_response = { error: "One or both query executions not found" }
    respond_to do |format|
      format.json { render json: error_response, status: :not_found }
      format.html { redirect_to root_path, alert: error_response[:error] }
    end
  rescue => e
    Rails.logger.error "Comparison failed: #{e.message}"
    Rails.logger.error e.backtrace.join("\n")

    error_response = { error: "Comparison failed: #{e.message}" }
    respond_to do |format|
      format.json { render json: error_response, status: :internal_server_error }
      format.html { redirect_to root_path, alert: error_response[:error] }
    end
  end
end

#execution_statusObject

GET /pg_insights/execution/:id



85
86
87
88
89
90
# File 'app/controllers/pg_insights/insights_controller.rb', line 85

def execution_status
  execution = QueryExecution.find(params[:id])
  render json: format_execution_response(execution)
rescue ActiveRecord::RecordNotFound
  render json: { error: "Execution not found" }, status: :not_found
end

#indexObject

GET /pg_insights POST /pg_insights



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
# File 'app/controllers/pg_insights/insights_controller.rb', line 12

def index
  # Combine built-in and user-saved queries for the UI
  built_in_queries = PgInsights::InsightQueryService.all

  saved_queries = PgInsights::Query.order(updated_at: :desc).map do |q|
    {
      id: q.id,
      name: q.name,
      sql: q.sql,
      description: q.description,
      category: q.category || "saved"
    }
  end

  @insight_queries = built_in_queries + saved_queries

  return unless request.post?

  # Determine execution type from button clicked
  execution_type = determine_execution_type
  sql = params.require(:sql)

  unless read_only?(sql)
    flash.now[:alert] = "Only single SELECT statements are allowed"
    return render :index, status: :unprocessable_entity
  end

  # Handle different execution types
  case execution_type
  when "execute"
    handle_execute_only(sql)
  when "analyze"
    handle_analyze_only(sql)
  when "both"
    handle_execute_and_analyze(sql)
  else
    # Fallback to original behavior for backward compatibility
    handle_execute_only(sql)
  end

  render :index
end

#query_historyObject



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'app/controllers/pg_insights/insights_controller.rb', line 103

def query_history
  @executions = QueryExecution.recent_history(10)

  executions_data = @executions.map do |execution|
    {
      id: execution.id,
      title: execution.display_title,
      summary: execution.display_summary,
      performance_class: execution.performance_class,
      created_at: execution.created_at.strftime("%m/%d %H:%M"),
      total_time_ms: execution.total_time_ms,
      query_cost: execution.query_cost,
      sql_text: execution.sql_text
    }
  end

  respond_to do |format|
    format.json { render json: executions_data }
    format.html { redirect_to root_path }
  end
end

#table_namesObject

GET /pg_insights/table_names



93
94
95
96
97
98
99
100
101
# File 'app/controllers/pg_insights/insights_controller.rb', line 93

def table_names
  tables = ActiveRecord::Base.connection.exec_query(
    "SELECT tablename FROM pg_tables WHERE schemaname = 'public' ORDER BY tablename"
  )
  render json: { tables: tables.rows.map(&:first) }
rescue ActiveRecord::StatementInvalid, PG::Error => e
  Rails.logger.error "Failed to fetch table names: #{e.message}"
  render json: { tables: [] }
end