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.
- checksums.yaml +7 -0
- data/.github/workflows/ci.yml +53 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +13 -0
- data/Gemfile +15 -0
- data/LICENSE.txt +21 -0
- data/README.md +295 -0
- data/Rakefile +6 -0
- data/app/controllers/concerns/mysql_genius/ai_features.rb +360 -0
- data/app/controllers/concerns/mysql_genius/database_analysis.rb +259 -0
- data/app/controllers/concerns/mysql_genius/query_execution.rb +129 -0
- data/app/controllers/mysql_genius/base_controller.rb +18 -0
- data/app/controllers/mysql_genius/queries_controller.rb +54 -0
- data/app/services/mysql_genius/ai_client.rb +84 -0
- data/app/services/mysql_genius/ai_optimization_service.rb +56 -0
- data/app/services/mysql_genius/ai_suggestion_service.rb +56 -0
- data/app/views/layouts/mysql_genius/application.html.erb +116 -0
- data/app/views/mysql_genius/queries/_shared_results.html.erb +56 -0
- data/app/views/mysql_genius/queries/_tab_ai_tools.html.erb +43 -0
- data/app/views/mysql_genius/queries/_tab_duplicate_indexes.html.erb +24 -0
- data/app/views/mysql_genius/queries/_tab_query_stats.html.erb +36 -0
- data/app/views/mysql_genius/queries/_tab_server.html.erb +54 -0
- data/app/views/mysql_genius/queries/_tab_slow_queries.html.erb +17 -0
- data/app/views/mysql_genius/queries/_tab_sql_query.html.erb +40 -0
- data/app/views/mysql_genius/queries/_tab_table_sizes.html.erb +31 -0
- data/app/views/mysql_genius/queries/_tab_unused_indexes.html.erb +25 -0
- data/app/views/mysql_genius/queries/_tab_visual_builder.html.erb +61 -0
- data/app/views/mysql_genius/queries/index.html.erb +1185 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/config/routes.rb +24 -0
- data/docs/screenshots/ai_tools.png +0 -0
- data/docs/screenshots/duplicate_indexes.png +0 -0
- data/docs/screenshots/query_stats.png +0 -0
- data/docs/screenshots/server.png +0 -0
- data/docs/screenshots/sql_query.png +0 -0
- data/docs/screenshots/table_sizes.png +0 -0
- data/docs/screenshots/visual_builder.png +0 -0
- data/lib/mysql_genius/configuration.rb +96 -0
- data/lib/mysql_genius/engine.rb +12 -0
- data/lib/mysql_genius/slow_query_monitor.rb +38 -0
- data/lib/mysql_genius/sql_validator.rb +55 -0
- data/lib/mysql_genius/version.rb +3 -0
- data/lib/mysql_genius.rb +23 -0
- data/mysql_genius.gemspec +34 -0
- metadata +122 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<!-- Query Stats Tab -->
|
|
2
|
+
<div class="mg-tab-content" id="tab-qstats">
|
|
3
|
+
<div class="mg-row" style="justify-content:space-between;align-items:center;margin-bottom:12px;">
|
|
4
|
+
<div class="mg-text-muted">Top queries from <code>performance_schema.events_statements_summary_by_digest</code>. <span id="qstats-count" class="mg-badge mg-badge-secondary"></span></div>
|
|
5
|
+
<div>
|
|
6
|
+
<label style="display:inline;font-size:12px;margin-right:4px;">Sort by:</label>
|
|
7
|
+
<select id="qstats-sort" style="width:auto;display:inline-block;padding:3px 6px;font-size:12px;">
|
|
8
|
+
<option value="total_time">Total Time</option>
|
|
9
|
+
<option value="avg_time">Avg Time</option>
|
|
10
|
+
<option value="calls">Calls</option>
|
|
11
|
+
<option value="rows_examined">Rows Examined</option>
|
|
12
|
+
</select>
|
|
13
|
+
<button id="qstats-refresh" class="mg-btn mg-btn-outline-secondary mg-btn-sm" style="margin-left:4px;">↻ Refresh</button>
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
<div id="qstats-loading" class="mg-text-center mg-hidden"><span class="mg-spinner"></span> Loading...</div>
|
|
17
|
+
<div id="qstats-error" class="mg-hidden"></div>
|
|
18
|
+
<div id="qstats-empty" class="mg-text-center mg-text-muted mg-hidden">No query statistics available.</div>
|
|
19
|
+
<div id="qstats-table-wrapper" class="mg-table-wrap mg-hidden">
|
|
20
|
+
<table class="mg-table">
|
|
21
|
+
<thead>
|
|
22
|
+
<tr>
|
|
23
|
+
<th>Query</th>
|
|
24
|
+
<th style="text-align:right">Calls</th>
|
|
25
|
+
<th style="text-align:right">Total Time</th>
|
|
26
|
+
<th style="text-align:right">Avg Time</th>
|
|
27
|
+
<th style="text-align:right">Max Time</th>
|
|
28
|
+
<th style="text-align:right">Rows Examined</th>
|
|
29
|
+
<th style="text-align:right">Rows Sent</th>
|
|
30
|
+
<th style="text-align:right">Exam/Sent</th>
|
|
31
|
+
</tr>
|
|
32
|
+
</thead>
|
|
33
|
+
<tbody id="qstats-tbody"></tbody>
|
|
34
|
+
</table>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<!-- Server Tab -->
|
|
2
|
+
<div class="mg-tab-content" id="tab-server">
|
|
3
|
+
<div class="mg-row" style="justify-content:flex-end;margin-bottom:12px;">
|
|
4
|
+
<% if @ai_enabled %>
|
|
5
|
+
<button id="server-root-cause" class="mg-btn mg-btn-primary mg-btn-sm">⚡ Why is it slow?</button>
|
|
6
|
+
<button id="server-anomaly" class="mg-btn mg-btn-outline mg-btn-sm">⚡ Anomaly Detection</button>
|
|
7
|
+
<% end %>
|
|
8
|
+
<button id="server-refresh" class="mg-btn mg-btn-outline-secondary mg-btn-sm">↻ Refresh</button>
|
|
9
|
+
</div>
|
|
10
|
+
<div id="server-loading" class="mg-text-center mg-hidden"><span class="mg-spinner"></span> Loading...</div>
|
|
11
|
+
<div id="server-error" class="mg-hidden"></div>
|
|
12
|
+
<div id="server-content" class="mg-hidden">
|
|
13
|
+
<!-- Server Info -->
|
|
14
|
+
<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:12px;margin-bottom:16px;">
|
|
15
|
+
<div class="mg-card">
|
|
16
|
+
<div class="mg-card-header"><strong>Server</strong></div>
|
|
17
|
+
<div class="mg-card-body">
|
|
18
|
+
<div class="mg-stat-grid" id="server-info"></div>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
<div class="mg-card">
|
|
22
|
+
<div class="mg-card-header"><strong>Connections</strong></div>
|
|
23
|
+
<div class="mg-card-body">
|
|
24
|
+
<div id="conn-bar" style="margin-bottom:8px;"></div>
|
|
25
|
+
<div class="mg-stat-grid" id="conn-info"></div>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
<div class="mg-card">
|
|
29
|
+
<div class="mg-card-header"><strong>InnoDB Buffer Pool</strong></div>
|
|
30
|
+
<div class="mg-card-body">
|
|
31
|
+
<div id="innodb-bar" style="margin-bottom:8px;"></div>
|
|
32
|
+
<div class="mg-stat-grid" id="innodb-info"></div>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
<div class="mg-card">
|
|
36
|
+
<div class="mg-card-header"><strong>Query Activity</strong></div>
|
|
37
|
+
<div class="mg-card-body">
|
|
38
|
+
<div class="mg-stat-grid" id="query-info"></div>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
<div id="server-ai-result" class="mg-mt mg-hidden">
|
|
43
|
+
<div class="mg-card">
|
|
44
|
+
<div class="mg-card-header">
|
|
45
|
+
<span id="server-ai-title"><strong>⚡ AI Analysis</strong></span>
|
|
46
|
+
<button id="server-ai-close" class="mg-btn mg-btn-outline-secondary mg-btn-sm">✕</button>
|
|
47
|
+
</div>
|
|
48
|
+
<div class="mg-card-body">
|
|
49
|
+
<div id="server-ai-content" style="font-size:13px;"></div>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!-- Slow Queries Tab -->
|
|
2
|
+
<div class="mg-tab-content" id="tab-slow">
|
|
3
|
+
<div class="mg-row" style="justify-content:space-between;align-items:center;margin-bottom:12px;">
|
|
4
|
+
<div class="mg-text-muted">SELECT queries exceeding <%= MysqlGenius.configuration.slow_query_threshold_ms %>ms. <span id="slow-count" class="mg-badge mg-badge-secondary"></span></div>
|
|
5
|
+
<button id="slow-refresh" class="mg-btn mg-btn-outline-secondary mg-btn-sm">↻ Refresh</button>
|
|
6
|
+
</div>
|
|
7
|
+
<div id="slow-loading" class="mg-text-center mg-hidden"><span class="mg-spinner"></span> Loading...</div>
|
|
8
|
+
<div id="slow-empty" class="mg-text-center mg-text-muted mg-hidden">No slow queries recorded.</div>
|
|
9
|
+
<div id="slow-table-wrapper" class="mg-table-wrap mg-hidden">
|
|
10
|
+
<table class="mg-table">
|
|
11
|
+
<thead>
|
|
12
|
+
<tr><th style="width:100px">Duration</th><th style="width:160px">Time</th><th>SQL</th><th style="width:150px">Actions</th></tr>
|
|
13
|
+
</thead>
|
|
14
|
+
<tbody id="slow-tbody"></tbody>
|
|
15
|
+
</table>
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<!-- SQL Query Tab -->
|
|
2
|
+
<div class="mg-tab-content" id="tab-sql">
|
|
3
|
+
<% if @ai_enabled %>
|
|
4
|
+
<div class="mg-card mg-mb">
|
|
5
|
+
<div class="mg-card-header">
|
|
6
|
+
<span class="mg-card-toggle" id="ai-toggle">⚡ AI Assistant <span class="mg-text-muted">- Describe what you want in plain English</span></span>
|
|
7
|
+
</div>
|
|
8
|
+
<div class="mg-card-body mg-hidden" id="ai-panel">
|
|
9
|
+
<div class="mg-field">
|
|
10
|
+
<textarea id="ai-prompt" rows="2" placeholder="e.g. Show me all users created in the last 30 days"></textarea>
|
|
11
|
+
<div class="mg-text-muted" style="margin-top:2px;">Only table names and column names are sent to the AI. No row data leaves the system.</div>
|
|
12
|
+
</div>
|
|
13
|
+
<button id="ai-suggest" class="mg-btn mg-btn-primary mg-btn-sm mg-mb">⚡ Suggest Query</button>
|
|
14
|
+
<div id="ai-result" class="mg-hidden">
|
|
15
|
+
<div id="ai-explanation" class="mg-alert mg-alert-info"></div>
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
<% end %>
|
|
20
|
+
|
|
21
|
+
<div class="mg-field">
|
|
22
|
+
<label for="sql-input">SQL Query</label>
|
|
23
|
+
<textarea id="sql-input" rows="5" placeholder="SELECT * FROM users LIMIT 10"></textarea>
|
|
24
|
+
</div>
|
|
25
|
+
<div class="mg-row">
|
|
26
|
+
<div class="mg-col-2 mg-field">
|
|
27
|
+
<label for="sql-row-limit">Row Limit</label>
|
|
28
|
+
<input type="number" id="sql-row-limit" value="25" min="1" max="<%= MysqlGenius.configuration.max_row_limit %>">
|
|
29
|
+
</div>
|
|
30
|
+
<div class="mg-field">
|
|
31
|
+
<label> </label>
|
|
32
|
+
<button id="sql-run" class="mg-btn mg-btn-primary">▶ Run Query</button>
|
|
33
|
+
<button id="sql-explain" class="mg-btn mg-btn-outline">🔎 Explain</button>
|
|
34
|
+
<% if @ai_enabled %>
|
|
35
|
+
<button id="sql-describe" class="mg-btn mg-btn-outline mg-btn-sm" style="margin-left:8px;">⚡ Describe</button>
|
|
36
|
+
<button id="sql-rewrite" class="mg-btn mg-btn-outline mg-btn-sm">⚡ Rewrite</button>
|
|
37
|
+
<% end %>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<!-- Table Sizes Tab -->
|
|
2
|
+
<div class="mg-tab-content" id="tab-sizes">
|
|
3
|
+
<div class="mg-row" style="justify-content:space-between;align-items:center;margin-bottom:12px;">
|
|
4
|
+
<div class="mg-text-muted">Table sizes from <code>information_schema</code>, sorted by total size. Row counts are estimates. <span id="sizes-total" class="mg-badge mg-badge-secondary"></span></div>
|
|
5
|
+
<button id="sizes-refresh" class="mg-btn mg-btn-outline-secondary mg-btn-sm">↻ Refresh</button>
|
|
6
|
+
</div>
|
|
7
|
+
<div id="sizes-loading" class="mg-text-center mg-hidden"><span class="mg-spinner"></span> Loading...</div>
|
|
8
|
+
<div id="sizes-chart-wrapper" class="mg-hidden" style="margin-bottom:16px;">
|
|
9
|
+
<div style="display:flex;align-items:center;gap:24px;flex-wrap:wrap;">
|
|
10
|
+
<canvas id="sizes-pie" width="280" height="280" style="flex-shrink:0;"></canvas>
|
|
11
|
+
<div id="sizes-legend" style="font-size:12px;line-height:1.8;"></div>
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
<div id="sizes-table-wrapper" class="mg-table-wrap mg-hidden">
|
|
15
|
+
<table class="mg-table">
|
|
16
|
+
<thead>
|
|
17
|
+
<tr>
|
|
18
|
+
<th>Table</th>
|
|
19
|
+
<th style="text-align:right">Rows (est.)</th>
|
|
20
|
+
<th style="text-align:right">Data</th>
|
|
21
|
+
<th style="text-align:right">Indexes</th>
|
|
22
|
+
<th style="text-align:right">Total</th>
|
|
23
|
+
<th style="text-align:right">Fragmented</th>
|
|
24
|
+
<th style="width:200px"></th>
|
|
25
|
+
</tr>
|
|
26
|
+
</thead>
|
|
27
|
+
<tbody id="sizes-tbody"></tbody>
|
|
28
|
+
</table>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
</div>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<!-- Unused Indexes Tab -->
|
|
2
|
+
<div class="mg-tab-content" id="tab-unused">
|
|
3
|
+
<div class="mg-row" style="justify-content:space-between;align-items:center;margin-bottom:12px;">
|
|
4
|
+
<div class="mg-text-muted">Indexes with zero reads since last server restart, from <code>performance_schema</code>. <span id="unused-count" class="mg-badge mg-badge-secondary"></span></div>
|
|
5
|
+
<button id="unused-refresh" class="mg-btn mg-btn-outline-secondary mg-btn-sm">↻ Refresh</button>
|
|
6
|
+
</div>
|
|
7
|
+
<div id="unused-loading" class="mg-text-center mg-hidden"><span class="mg-spinner"></span> Scanning...</div>
|
|
8
|
+
<div id="unused-error" class="mg-hidden"></div>
|
|
9
|
+
<div id="unused-empty" class="mg-text-center mg-text-muted mg-hidden">No unused indexes found.</div>
|
|
10
|
+
<div id="unused-table-wrapper" class="mg-table-wrap mg-hidden">
|
|
11
|
+
<table class="mg-table">
|
|
12
|
+
<thead>
|
|
13
|
+
<tr>
|
|
14
|
+
<th>Table</th>
|
|
15
|
+
<th>Index</th>
|
|
16
|
+
<th style="text-align:right">Reads</th>
|
|
17
|
+
<th style="text-align:right">Writes</th>
|
|
18
|
+
<th style="text-align:right">Table Rows</th>
|
|
19
|
+
<th>DROP Statement</th>
|
|
20
|
+
</tr>
|
|
21
|
+
</thead>
|
|
22
|
+
<tbody id="unused-tbody"></tbody>
|
|
23
|
+
</table>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<!-- Visual Builder Tab -->
|
|
2
|
+
<div class="mg-tab-content active" id="tab-visual">
|
|
3
|
+
<div class="mg-row mg-mb">
|
|
4
|
+
<div class="mg-col-4 mg-field">
|
|
5
|
+
<label for="vb-table">Table</label>
|
|
6
|
+
<select id="vb-table">
|
|
7
|
+
<option value="">-- Select a table --</option>
|
|
8
|
+
<% if @featured_tables != @all_tables %>
|
|
9
|
+
<optgroup label="Featured">
|
|
10
|
+
<% @featured_tables.each do |table| %>
|
|
11
|
+
<option value="<%= table %>"><%= table %></option>
|
|
12
|
+
<% end %>
|
|
13
|
+
</optgroup>
|
|
14
|
+
<optgroup label="All Tables">
|
|
15
|
+
<% (@all_tables - @featured_tables).each do |table| %>
|
|
16
|
+
<option value="<%= table %>"><%= table %></option>
|
|
17
|
+
<% end %>
|
|
18
|
+
</optgroup>
|
|
19
|
+
<% else %>
|
|
20
|
+
<% @all_tables.each do |table| %>
|
|
21
|
+
<option value="<%= table %>"><%= table %></option>
|
|
22
|
+
<% end %>
|
|
23
|
+
<% end %>
|
|
24
|
+
</select>
|
|
25
|
+
</div>
|
|
26
|
+
<div class="mg-col-2 mg-field">
|
|
27
|
+
<label for="vb-row-limit">Row Limit</label>
|
|
28
|
+
<input type="number" id="vb-row-limit" value="25" min="1" max="<%= MysqlGenius.configuration.max_row_limit %>">
|
|
29
|
+
</div>
|
|
30
|
+
<div class="mg-field">
|
|
31
|
+
<label> </label>
|
|
32
|
+
<button id="vb-run" class="mg-btn mg-btn-primary" disabled>▶ Run Query</button>
|
|
33
|
+
<button id="vb-explain" class="mg-btn mg-btn-outline" disabled>🔎 Explain</button>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<div id="vb-columns-section" class="mg-mb mg-hidden">
|
|
38
|
+
<label>Columns
|
|
39
|
+
<span id="vb-toggle-all" class="mg-link">Toggle All</span>
|
|
40
|
+
<span id="vb-show-defaults" class="mg-link">Reset Defaults</span>
|
|
41
|
+
</label>
|
|
42
|
+
<div id="vb-columns" class="mg-checks"></div>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<div id="vb-filters-section" class="mg-mb mg-hidden">
|
|
46
|
+
<label>Filters</label>
|
|
47
|
+
<div id="vb-filters"></div>
|
|
48
|
+
<button id="vb-add-filter" class="mg-btn mg-btn-outline-secondary mg-btn-sm" style="margin-top:4px;">+ Add Filter</button>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<div id="vb-order-section" class="mg-mb mg-hidden">
|
|
52
|
+
<label>Order By</label>
|
|
53
|
+
<div id="vb-orders"></div>
|
|
54
|
+
<button id="vb-add-order" class="mg-btn mg-btn-outline-secondary mg-btn-sm" style="margin-top:4px;">+ Add Sort</button>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<div id="vb-generated-sql" class="mg-mb mg-hidden">
|
|
58
|
+
<label>Generated SQL</label>
|
|
59
|
+
<textarea id="vb-sql-preview" rows="2" readonly></textarea>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|