mysql_genius 0.3.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/publish.yml +21 -2
- data/.gitignore +1 -0
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +18 -0
- data/Gemfile +2 -0
- data/README.md +27 -0
- data/app/controllers/concerns/mysql_genius/ai_features.rb +31 -11
- data/app/controllers/concerns/mysql_genius/database_analysis.rb +15 -263
- data/app/controllers/concerns/mysql_genius/query_execution.rb +39 -81
- data/lib/mysql_genius/core/connection/active_record_adapter.rb +77 -0
- data/lib/mysql_genius/version.rb +1 -1
- data/lib/mysql_genius.rb +2 -1
- data/mysql_genius.gemspec +2 -1
- metadata +16 -7
- data/app/services/mysql_genius/ai_client.rb +0 -91
- data/app/services/mysql_genius/ai_optimization_service.rb +0 -60
- data/app/services/mysql_genius/ai_suggestion_service.rb +0 -59
- data/docs/superpowers/plans/2026-04-08-dashboard-first-redesign.md +0 -741
- data/docs/superpowers/specs/2026-04-08-dashboard-first-redesign.md +0 -87
- data/lib/mysql_genius/sql_validator.rb +0 -57
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
# Dashboard-First Redesign
|
|
2
|
-
|
|
3
|
-
Redesign the default landing experience to lead with PgHero-style monitoring instead of the SQL query builder. Based on Reddit feedback that the current Visual Builder default makes the engine look like "another DB GUI" rather than a MySQL performance dashboard.
|
|
4
|
-
|
|
5
|
-
## Changes
|
|
6
|
-
|
|
7
|
-
### 1. New Dashboard Tab (default)
|
|
8
|
-
|
|
9
|
-
A new tab that becomes the active tab on page load. Loads all data immediately via parallel AJAX calls to existing endpoints. Four stacked sections:
|
|
10
|
-
|
|
11
|
-
**Server Summary** (reuses `server_overview` endpoint)
|
|
12
|
-
- Four cards in a responsive grid: Server (version, uptime), Connections (used/max progress bar), InnoDB Buffer Pool (hit rate progress bar), Query Activity (questions/sec, slow queries count)
|
|
13
|
-
|
|
14
|
-
**Top 5 Slow Queries** (reuses `slow_queries` endpoint)
|
|
15
|
-
- Compact table: duration, timestamp, truncated SQL
|
|
16
|
-
- "View all" link switches to Slow Queries tab
|
|
17
|
-
- If Redis not configured: muted placeholder "Configure `redis_url` to monitor slow queries."
|
|
18
|
-
|
|
19
|
-
**Top 5 Expensive Queries** (reuses `query_stats` endpoint with new `limit` param)
|
|
20
|
-
- Compact table: query digest, calls, total time, avg time
|
|
21
|
-
- Sorted by total time descending
|
|
22
|
-
- "View all" link switches to Query Stats tab
|
|
23
|
-
- If `performance_schema` unavailable: muted placeholder
|
|
24
|
-
|
|
25
|
-
**Index Alerts** (reuses `duplicate_indexes` and `unused_indexes` endpoints)
|
|
26
|
-
- Two badge cards showing counts: "X duplicate indexes", "X unused indexes"
|
|
27
|
-
- Green checkmark if count is zero
|
|
28
|
-
- Each badge is clickable, switches to the corresponding tab
|
|
29
|
-
|
|
30
|
-
### 2. Query Explorer Tab (merged Visual Builder + SQL Query)
|
|
31
|
-
|
|
32
|
-
The two existing query tabs merge into one "Query Explorer" tab with a mode toggle:
|
|
33
|
-
|
|
34
|
-
- **Visual mode** -- existing visual builder UI (table selector, columns, filters, ordering)
|
|
35
|
-
- **SQL mode** -- existing raw SQL textarea with AI suggestion panel
|
|
36
|
-
- Small toggle buttons at top of tab to switch modes
|
|
37
|
-
- Default mode: Visual
|
|
38
|
-
- SQL generated by visual builder carries over when switching to SQL mode (existing behavior preserved)
|
|
39
|
-
- Shared results area unchanged
|
|
40
|
-
|
|
41
|
-
### 3. Tab Reorder
|
|
42
|
-
|
|
43
|
-
New order:
|
|
44
|
-
1. Dashboard (new, default active)
|
|
45
|
-
2. Slow Queries
|
|
46
|
-
3. Query Stats
|
|
47
|
-
4. Server
|
|
48
|
-
5. Table Sizes
|
|
49
|
-
6. Unused Indexes
|
|
50
|
-
7. Duplicate Indexes
|
|
51
|
-
8. Query Explorer (merged)
|
|
52
|
-
9. AI Tools (conditional on `@ai_enabled`)
|
|
53
|
-
|
|
54
|
-
### 4. Backend Change
|
|
55
|
-
|
|
56
|
-
Add `limit` parameter support to the `query_stats` action. When present, cap the result set to the given limit. The dashboard passes `limit=5`; the Query Stats tab continues to omit it for the full list.
|
|
57
|
-
|
|
58
|
-
### 5. Gemspec Description Update
|
|
59
|
-
|
|
60
|
-
Change description to lead with monitoring:
|
|
61
|
-
> "MysqlGenius gives Rails apps a mountable performance dashboard for MySQL databases. Monitor slow queries, analyze query statistics from performance_schema, detect unused and duplicate indexes, and explore your database with optional AI-powered optimization."
|
|
62
|
-
|
|
63
|
-
### 6. README Updates
|
|
64
|
-
|
|
65
|
-
- Reorder screenshots section to lead with Dashboard
|
|
66
|
-
- Update "Usage" section to reflect new tab order and dashboard-first experience
|
|
67
|
-
- Add Dashboard screenshot placeholder
|
|
68
|
-
|
|
69
|
-
## Not Changing
|
|
70
|
-
|
|
71
|
-
- No new routes -- dashboard reuses existing endpoints
|
|
72
|
-
- No new controller actions -- only a `limit` param added to `query_stats`
|
|
73
|
-
- All existing tab partials remain intact, just reordered in the view
|
|
74
|
-
- Shared results area unchanged
|
|
75
|
-
- AI features unchanged
|
|
76
|
-
- Authentication unchanged
|
|
77
|
-
|
|
78
|
-
## Files Affected
|
|
79
|
-
|
|
80
|
-
- `app/views/mysql_genius/queries/index.html.erb` -- tab bar reorder, new dashboard partial render, merge visual/sql tabs, JS changes for dashboard auto-load and query explorer toggle
|
|
81
|
-
- `app/views/mysql_genius/queries/_tab_dashboard.html.erb` -- new partial
|
|
82
|
-
- `app/views/mysql_genius/queries/_tab_query_explorer.html.erb` -- new partial combining visual builder and sql query content
|
|
83
|
-
- `app/views/mysql_genius/queries/_tab_visual_builder.html.erb` -- removed (content moves to query explorer)
|
|
84
|
-
- `app/views/mysql_genius/queries/_tab_sql_query.html.erb` -- removed (content moves to query explorer)
|
|
85
|
-
- `app/controllers/concerns/mysql_genius/database_analysis.rb` -- add `limit` param to `query_stats`
|
|
86
|
-
- `mysql_genius.gemspec` -- updated description
|
|
87
|
-
- `README.md` -- updated screenshots order, usage section
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module MysqlGenius
|
|
4
|
-
module SqlValidator
|
|
5
|
-
FORBIDDEN_KEYWORDS = ["INSERT", "UPDATE", "DELETE", "DROP", "ALTER", "CREATE", "TRUNCATE", "GRANT", "REVOKE"].freeze
|
|
6
|
-
|
|
7
|
-
extend self
|
|
8
|
-
|
|
9
|
-
def validate(sql, blocked_tables:, connection:)
|
|
10
|
-
return "Please enter a query." if sql.nil? || sql.strip.empty?
|
|
11
|
-
|
|
12
|
-
normalized = sql.gsub(/--.*$/, "").gsub(%r{/\*.*?\*/}m, "").strip
|
|
13
|
-
|
|
14
|
-
unless normalized.match?(/\ASELECT\b/i) || normalized.match?(/\AWITH\b/i)
|
|
15
|
-
return "Only SELECT queries are allowed."
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
return "Access to system schemas is not allowed." if normalized.match?(/\b(information_schema|mysql|performance_schema|sys)\b/i)
|
|
19
|
-
|
|
20
|
-
FORBIDDEN_KEYWORDS.each do |keyword|
|
|
21
|
-
return "#{keyword} statements are not allowed." if normalized.match?(/\b#{keyword}\b/i)
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
tables_in_query = extract_table_references(normalized, connection)
|
|
25
|
-
blocked = tables_in_query & blocked_tables
|
|
26
|
-
if blocked.any?
|
|
27
|
-
return "Access denied for table(s): #{blocked.join(", ")}."
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
nil
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def extract_table_references(sql, connection)
|
|
34
|
-
tables = []
|
|
35
|
-
sql.scan(/\bFROM\s+((?:`?\w+`?(?:\s*,\s*`?\w+`?)*)+)/i) { |m| m[0].scan(/`?(\w+)`?/) { |t| tables << t[0] } }
|
|
36
|
-
sql.scan(/\bJOIN\s+`?(\w+)`?/i) { |m| tables << m[0] }
|
|
37
|
-
sql.scan(/\b(?:INTO|UPDATE)\s+`?(\w+)`?/i) { |m| tables << m[0] }
|
|
38
|
-
tables.uniq.map(&:downcase) & connection.tables
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def apply_row_limit(sql, limit)
|
|
42
|
-
if sql.match?(/\bLIMIT\s+\d+\s*,\s*\d+/i)
|
|
43
|
-
sql.gsub(/\bLIMIT\s+(\d+)\s*,\s*(\d+)/i) do
|
|
44
|
-
"LIMIT #{::Regexp.last_match(1).to_i}, #{[::Regexp.last_match(2).to_i, limit].min}"
|
|
45
|
-
end
|
|
46
|
-
elsif sql.match?(/\bLIMIT\s+\d+/i)
|
|
47
|
-
sql.gsub(/\bLIMIT\s+(\d+)/i) { "LIMIT #{[::Regexp.last_match(1).to_i, limit].min}" }
|
|
48
|
-
else
|
|
49
|
-
"#{sql.gsub(/;\s*\z/, "")} LIMIT #{limit}"
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def masked_column?(column_name, patterns)
|
|
54
|
-
patterns.any? { |pattern| column_name.downcase.include?(pattern) }
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
end
|