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.
@@ -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