mysql_genius 0.1.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.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/ci.yml +53 -0
  3. data/.gitignore +11 -0
  4. data/.rspec +3 -0
  5. data/CHANGELOG.md +13 -0
  6. data/Gemfile +15 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +295 -0
  9. data/Rakefile +6 -0
  10. data/app/controllers/concerns/mysql_genius/ai_features.rb +360 -0
  11. data/app/controllers/concerns/mysql_genius/database_analysis.rb +259 -0
  12. data/app/controllers/concerns/mysql_genius/query_execution.rb +129 -0
  13. data/app/controllers/mysql_genius/base_controller.rb +18 -0
  14. data/app/controllers/mysql_genius/queries_controller.rb +54 -0
  15. data/app/services/mysql_genius/ai_client.rb +84 -0
  16. data/app/services/mysql_genius/ai_optimization_service.rb +56 -0
  17. data/app/services/mysql_genius/ai_suggestion_service.rb +56 -0
  18. data/app/views/layouts/mysql_genius/application.html.erb +116 -0
  19. data/app/views/mysql_genius/queries/_shared_results.html.erb +56 -0
  20. data/app/views/mysql_genius/queries/_tab_ai_tools.html.erb +43 -0
  21. data/app/views/mysql_genius/queries/_tab_duplicate_indexes.html.erb +24 -0
  22. data/app/views/mysql_genius/queries/_tab_query_stats.html.erb +36 -0
  23. data/app/views/mysql_genius/queries/_tab_server.html.erb +54 -0
  24. data/app/views/mysql_genius/queries/_tab_slow_queries.html.erb +17 -0
  25. data/app/views/mysql_genius/queries/_tab_sql_query.html.erb +40 -0
  26. data/app/views/mysql_genius/queries/_tab_table_sizes.html.erb +31 -0
  27. data/app/views/mysql_genius/queries/_tab_unused_indexes.html.erb +25 -0
  28. data/app/views/mysql_genius/queries/_tab_visual_builder.html.erb +61 -0
  29. data/app/views/mysql_genius/queries/index.html.erb +1185 -0
  30. data/bin/console +14 -0
  31. data/bin/setup +8 -0
  32. data/config/routes.rb +24 -0
  33. data/docs/screenshots/ai_tools.png +0 -0
  34. data/docs/screenshots/duplicate_indexes.png +0 -0
  35. data/docs/screenshots/query_stats.png +0 -0
  36. data/docs/screenshots/server.png +0 -0
  37. data/docs/screenshots/sql_query.png +0 -0
  38. data/docs/screenshots/table_sizes.png +0 -0
  39. data/docs/screenshots/visual_builder.png +0 -0
  40. data/lib/mysql_genius/configuration.rb +96 -0
  41. data/lib/mysql_genius/engine.rb +12 -0
  42. data/lib/mysql_genius/slow_query_monitor.rb +38 -0
  43. data/lib/mysql_genius/sql_validator.rb +55 -0
  44. data/lib/mysql_genius/version.rb +3 -0
  45. data/lib/mysql_genius.rb +23 -0
  46. data/mysql_genius.gemspec +34 -0
  47. metadata +122 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 95e37df968649185800af1d3500a85a29fcb7b161f4b6fec56e0cb85f1989ddf
4
+ data.tar.gz: 024d6408ba0792c82692d47fa76daf74ffc9f81ed3340ea0f4bff2dd60494469
5
+ SHA512:
6
+ metadata.gz: a7935d42e665520dd8f3f3bd8e30c67b196760994522c9eb0ae63e3bc1a1606c0f98b4327392f4e0bc006470315b3a4467f5df583906359def790f9d0d4a7767
7
+ data.tar.gz: 33c9db66a13de66c1f68f9092834a8bc606dbf584d2cce2c647481155cf3321bcd4addcb6447ee622b2699ea80014ff687084dd03c841816a084a4279032d5e2
@@ -0,0 +1,53 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ ruby: ["2.7", "3.0", "3.1", "3.2", "3.3"]
17
+ rails: ["5.2", "6.0", "6.1", "7.0", "7.1", "7.2"]
18
+ exclude:
19
+ # Rails 7.0+ requires Ruby 2.7+, but 7.2 requires 3.1+
20
+ - ruby: "2.7"
21
+ rails: "7.2"
22
+ - ruby: "3.0"
23
+ rails: "7.2"
24
+ # Rails 5.2 doesn't support Ruby 3.1+
25
+ - ruby: "3.1"
26
+ rails: "5.2"
27
+ - ruby: "3.2"
28
+ rails: "5.2"
29
+ - ruby: "3.3"
30
+ rails: "5.2"
31
+ # Rails 6.0 doesn't support Ruby 3.2+
32
+ - ruby: "3.2"
33
+ rails: "6.0"
34
+ - ruby: "3.3"
35
+ rails: "6.0"
36
+
37
+ env:
38
+ RAILS_VERSION: ${{ matrix.rails }}
39
+
40
+ steps:
41
+ - uses: actions/checkout@v4
42
+
43
+ - name: Set up Ruby ${{ matrix.ruby }}
44
+ uses: ruby/setup-ruby@v1
45
+ with:
46
+ ruby-version: ${{ matrix.ruby }}
47
+ bundler-cache: true
48
+
49
+ - name: Install dependencies
50
+ run: bundle install
51
+
52
+ - name: Run specs
53
+ run: bundle exec rspec
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ - Initial release
6
+ - Visual query builder with column selection, filters, and ordering
7
+ - Safe SQL execution (read-only, blocked tables, masked columns, row limits, timeouts)
8
+ - EXPLAIN analysis
9
+ - AI-powered query suggestions (optional)
10
+ - AI-powered query optimization from EXPLAIN output (optional)
11
+ - Slow query monitoring via Redis
12
+ - Audit logging
13
+ - MariaDB support
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ if ENV["RAILS_VERSION"]
6
+ rails_version = ENV["RAILS_VERSION"]
7
+ gem "railties", "~> #{rails_version}.0"
8
+ gem "activerecord", "~> #{rails_version}.0"
9
+ gem "actionpack", "~> #{rails_version}.0"
10
+ end
11
+
12
+ group :development, :test do
13
+ gem "rake"
14
+ gem "rspec", "~> 3.0"
15
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Antarr Byrd
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,295 @@
1
+ # MySQLGenius
2
+
3
+ A MySQL performance dashboard and query explorer for Rails, inspired by [PgHero](https://github.com/ankane/pghero). If you've used PgHero for PostgreSQL and wished something similar existed for MySQL -- this is it, with AI-powered query suggestions and optimization on top.
4
+
5
+ ## Screenshots
6
+
7
+ ### Visual Builder
8
+ Build queries visually -- select tables, pick columns, add type-aware filters, and sort results without writing SQL.
9
+
10
+ ![Visual Builder](docs/screenshots/visual_builder.png)
11
+
12
+ ### SQL Query with AI Assistant
13
+ Write raw SQL or describe what you want in plain English and let the AI generate the query for you.
14
+
15
+ ![SQL Query](docs/screenshots/sql_query.png)
16
+
17
+ ### Duplicate Index Detection
18
+ Find redundant indexes whose columns are a left-prefix of another index, with ready-to-run `DROP INDEX` statements.
19
+
20
+ ![Duplicate Indexes](docs/screenshots/duplicate_indexes.png)
21
+
22
+ ### Table Sizes
23
+ View row counts, data size, index size, fragmentation, and a visual size chart for every table.
24
+
25
+ ![Table Sizes](docs/screenshots/table_sizes.png)
26
+
27
+ ### Query Stats
28
+ Top queries from `performance_schema` sorted by total time, with call counts, avg/max time, and rows examined.
29
+
30
+ ![Query Stats](docs/screenshots/query_stats.png)
31
+
32
+ ### Server Dashboard
33
+ At-a-glance server health: version, connections, InnoDB buffer pool, and query activity with AI-powered diagnostics.
34
+
35
+ ![Server](docs/screenshots/server.png)
36
+
37
+ ### AI Tools
38
+ Schema review that finds anti-patterns -- missing primary keys, nullable foreign keys, inappropriate column types, and more.
39
+
40
+ ![AI Tools](docs/screenshots/ai_tools.png)
41
+
42
+ ## Features
43
+
44
+ - **Visual Query Builder** -- point-and-click query construction with column selection, type-aware filters, and ordering
45
+ - **Safe SQL Execution** -- read-only enforcement, blocked tables, masked sensitive columns, row limits, query timeouts
46
+ - **EXPLAIN Analysis** -- run EXPLAIN on any query and view the execution plan
47
+ - **AI Query Suggestions** -- describe what you want in plain English, get SQL back (optional, any OpenAI-compatible API)
48
+ - **AI Query Optimization** -- get actionable optimization suggestions from EXPLAIN output (optional)
49
+ - **Slow Query Monitoring** -- captures slow SELECT queries via ActiveSupport notifications and Redis
50
+ - **Duplicate Index Detection** -- finds redundant indexes whose columns are a left-prefix of another index
51
+ - **Table Size Dashboard** -- view row counts, data size, index size, and fragmentation for all tables
52
+ - **Audit Logging** -- logs all query executions, rejections, and errors
53
+ - **MariaDB Support** -- automatically detects MariaDB and uses appropriate timeout syntax
54
+ - **Self-contained UI** -- no external CSS/JS dependencies, works with any Rails layout
55
+ - **Zero jQuery** -- pure vanilla JavaScript frontend
56
+
57
+ ## Requirements
58
+
59
+ - Rails 5.2+
60
+ - Ruby 2.6+
61
+ - MySQL or MariaDB
62
+ - Redis (optional, for slow query monitoring)
63
+
64
+ ## Installation
65
+
66
+ Add to your Gemfile:
67
+
68
+ ```ruby
69
+ gem "mysql_genius"
70
+ ```
71
+
72
+ Or from GitHub:
73
+
74
+ ```ruby
75
+ gem "mysql_genius", github: "antarr/mysql_genius"
76
+ ```
77
+
78
+ Then run:
79
+
80
+ ```
81
+ bundle install
82
+ ```
83
+
84
+ ## Setup
85
+
86
+ ### 1. Mount the engine
87
+
88
+ In `config/routes.rb`:
89
+
90
+ ```ruby
91
+ Rails.application.routes.draw do
92
+ mount MysqlGenius::Engine, at: "/mysql_genius"
93
+ end
94
+ ```
95
+
96
+ To restrict access at the route level:
97
+
98
+ ```ruby
99
+ # Using a session constraint
100
+ constraints ->(req) { req.session[:admin] } do
101
+ mount MysqlGenius::Engine, at: "/mysql_genius"
102
+ end
103
+
104
+ # Or using Devise
105
+ authenticate :user, ->(u) { u.admin? } do
106
+ mount MysqlGenius::Engine, at: "/mysql_genius"
107
+ end
108
+ ```
109
+
110
+ ### 2. Configure
111
+
112
+ Create `config/initializers/mysql_genius.rb`:
113
+
114
+ ```ruby
115
+ MysqlGenius.configure do |config|
116
+ # --- Authentication ---
117
+ # Lambda that receives the controller instance. Return true to allow access.
118
+ # Default: allows everyone. Use route constraints for most cases.
119
+ config.authenticate = ->(controller) { true }
120
+
121
+ # To use current_user or other app helpers, inherit from ApplicationController:
122
+ # config.base_controller = "ApplicationController"
123
+ # config.authenticate = ->(controller) { controller.current_user&.admin? }
124
+
125
+ # --- Tables ---
126
+ # Tables featured at the top of the visual builder dropdown (optional)
127
+ config.featured_tables = %w[users posts comments]
128
+
129
+ # Tables blocked from querying (defaults: sessions, schema_migrations, ar_internal_metadata)
130
+ config.blocked_tables += %w[oauth_tokens api_keys]
131
+
132
+ # Column patterns to redact with [REDACTED] in results (case-insensitive substring match)
133
+ config.masked_column_patterns = %w[password secret digest token ssn]
134
+
135
+ # Default columns checked in the visual builder per table (optional).
136
+ # When empty for a table, all columns are checked by default.
137
+ config.default_columns = {
138
+ "users" => %w[id name email created_at],
139
+ "posts" => %w[id title user_id published_at]
140
+ }
141
+
142
+ # --- Query Safety ---
143
+ config.max_row_limit = 1000 # Hard cap on rows returned
144
+ config.default_row_limit = 25 # Default when no limit specified
145
+ config.query_timeout_ms = 30_000 # 30 second timeout (uses MariaDB or MySQL hints)
146
+
147
+ # --- Slow Query Monitoring ---
148
+ # Requires Redis. Set to nil to disable.
149
+ config.redis_url = ENV["REDIS_URL"].presence || "redis://127.0.0.1:6379/0"
150
+ config.slow_query_threshold_ms = 250
151
+
152
+ # --- Audit Logging ---
153
+ # Set to nil to disable. Logs query executions, rejections, and errors.
154
+ config.audit_logger = Logger.new(Rails.root.join("log", "mysql_genius.log"))
155
+ end
156
+ ```
157
+
158
+ ### 3. AI Features (optional)
159
+
160
+ MySQLGenius supports AI-powered query suggestions and optimization via any OpenAI-compatible API, including OpenAI, Azure OpenAI, Ollama Cloud, and local Ollama instances.
161
+
162
+ ```ruby
163
+ MysqlGenius.configure do |config|
164
+ # --- Option A: OpenAI ---
165
+ config.ai_endpoint = "https://api.openai.com/v1/chat/completions"
166
+ config.ai_api_key = ENV["OPENAI_API_KEY"]
167
+ config.ai_model = "gpt-4o"
168
+ config.ai_auth_style = :bearer
169
+
170
+ # --- Option B: Azure OpenAI ---
171
+ config.ai_endpoint = ENV["AZURE_OPENAI_ENDPOINT"] # Your deployment URL
172
+ config.ai_api_key = ENV["AZURE_OPENAI_API_KEY"]
173
+ config.ai_auth_style = :api_key # Default, uses api-key header
174
+
175
+ # --- Option C: Ollama Cloud ---
176
+ config.ai_endpoint = "https://api.ollama.com/v1/chat/completions"
177
+ config.ai_api_key = ENV["OLLAMA_API_KEY"]
178
+ config.ai_model = "gemma3:27b"
179
+ config.ai_auth_style = :bearer
180
+
181
+ # --- Option D: Local Ollama ---
182
+ config.ai_endpoint = "http://localhost:11434/v1/chat/completions"
183
+ config.ai_api_key = "ollama" # Any non-empty string
184
+ config.ai_model = "llama3"
185
+ config.ai_auth_style = :bearer
186
+
187
+ # --- Option E: Custom client ---
188
+ # Any callable that accepts messages: and temperature: kwargs
189
+ # and returns an OpenAI-compatible response hash.
190
+ config.ai_client = ->(messages:, temperature:) {
191
+ MyAiService.chat(messages, temperature: temperature)
192
+ }
193
+
194
+ # --- Domain Context ---
195
+ # Helps the AI understand your schema and generate better queries.
196
+ config.ai_system_context = <<~CONTEXT
197
+ This is an e-commerce database.
198
+ - `users` stores customer accounts. Primary key is `id`.
199
+ - `orders` tracks purchases. Linked to users via `user_id`.
200
+ - `products` contains the product catalog.
201
+ - Soft-deleted records have `deleted_at IS NOT NULL`.
202
+ CONTEXT
203
+ end
204
+ ```
205
+
206
+ | Option | `ai_auth_style` | `ai_model` | Notes |
207
+ |--------|-----------------|------------|-------|
208
+ | OpenAI | `:bearer` | Required (e.g. `gpt-4o`) | |
209
+ | Azure OpenAI | `:api_key` (default) | Optional (uses deployment default) | |
210
+ | Ollama Cloud | `:bearer` | Required (e.g. `gemma3:27b`) | Follows redirects automatically |
211
+ | Local Ollama | `:bearer` | Required | No API key validation |
212
+ | Custom client | N/A | N/A | You handle everything |
213
+
214
+ When AI is not configured, the AI Assistant panel and optimization buttons are hidden automatically.
215
+
216
+ ## Usage
217
+
218
+ Visit `/mysql_genius` in your browser. The dashboard has five tabs:
219
+
220
+ ### Visual Builder
221
+ Select a table, pick columns, add type-aware filters (dates get date pickers, booleans get dropdowns), add sort orders, and run queries -- no SQL knowledge required. The generated SQL is shown and synced with the SQL tab.
222
+
223
+ ### SQL Query
224
+ Write raw SQL directly. The optional AI Assistant lets you describe what you want in plain English and generates a query. AI-generated queries are synced back to the Visual Builder for further refinement.
225
+
226
+ ### Slow Queries
227
+ View slow SELECT queries captured from your application in real time. Each slow query can be:
228
+ - **Explained** -- run EXPLAIN to see the execution plan (bypasses blocked table restrictions)
229
+ - **Used** -- copy to the SQL tab for editing and re-running
230
+ - **Optimized** -- get AI-powered optimization suggestions with specific index and rewrite recommendations
231
+
232
+ ### Duplicate Indexes
233
+ Scans all tables for redundant indexes -- indexes whose columns are a left-prefix of another index on the same table. Shows the `ALTER TABLE ... DROP INDEX` statement for each duplicate, ready to copy and run.
234
+
235
+ ### Table Sizes
236
+ Displays every table sorted by total size, with columns for estimated row count, data size, index size, total size, and fragmented space. Includes a visual size bar for quick comparison.
237
+
238
+ ## Configuration Reference
239
+
240
+ | Option | Type | Default | Description |
241
+ |--------|------|---------|-------------|
242
+ | `authenticate` | Proc | `->(_) { true }` | Authorization check |
243
+ | `base_controller` | String | `"ActionController::Base"` | Parent controller class |
244
+ | `featured_tables` | Array | `[]` | Tables shown in Featured group |
245
+ | `blocked_tables` | Array | `[sessions, ...]` | Tables that cannot be queried |
246
+ | `masked_column_patterns` | Array | `[password, secret, ...]` | Column patterns to redact |
247
+ | `default_columns` | Hash | `{}` | Default checked columns per table |
248
+ | `max_row_limit` | Integer | `1000` | Maximum rows returned |
249
+ | `default_row_limit` | Integer | `25` | Default row limit |
250
+ | `query_timeout_ms` | Integer | `30000` | Query timeout in ms |
251
+ | `redis_url` | String | `nil` | Redis URL for slow query monitoring |
252
+ | `slow_query_threshold_ms` | Integer | `250` | Slow query threshold |
253
+ | `audit_logger` | Logger | `nil` | Logger for query audit trail |
254
+ | `ai_endpoint` | String | `nil` | AI API endpoint URL |
255
+ | `ai_api_key` | String | `nil` | AI API key |
256
+ | `ai_model` | String | `nil` | AI model name |
257
+ | `ai_auth_style` | Symbol | `:api_key` | `:bearer` or `:api_key` |
258
+ | `ai_client` | Proc | `nil` | Custom AI client callable |
259
+ | `ai_system_context` | String | `nil` | Domain context for AI prompts |
260
+
261
+ ## Compatibility
262
+
263
+ Tested against:
264
+
265
+ | Rails | Ruby |
266
+ |-------|------|
267
+ | 5.2 | 2.6, 2.7, 3.0 |
268
+ | 6.0 | 2.6, 2.7, 3.0, 3.1 |
269
+ | 6.1 | 2.6, 2.7, 3.0, 3.1, 3.2, 3.3 |
270
+ | 7.0 | 2.7, 3.0, 3.1, 3.2, 3.3 |
271
+ | 7.1 | 2.7, 3.0, 3.1, 3.2, 3.3 |
272
+ | 7.2 | 3.1, 3.2, 3.3 |
273
+
274
+ ## Development
275
+
276
+ ```
277
+ git clone https://github.com/antarr/mysql_genius.git
278
+ cd mysql_genius
279
+ bin/setup
280
+ bundle exec rspec
281
+ ```
282
+
283
+ To test against a specific Rails version:
284
+
285
+ ```
286
+ RAILS_VERSION=6.1 bundle update && bundle exec rspec
287
+ ```
288
+
289
+ ## Contributing
290
+
291
+ Bug reports and pull requests are welcome on GitHub at https://github.com/antarr/mysql_genius.
292
+
293
+ ## License
294
+
295
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec