mysql_genius-core 0.4.1 → 0.5.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/CHANGELOG.md +10 -0
- data/lib/mysql_genius/core/ai/config.rb +5 -2
- data/lib/mysql_genius/core/ai/describe_query.rb +41 -0
- data/lib/mysql_genius/core/ai/index_advisor.rb +43 -0
- data/lib/mysql_genius/core/ai/migration_risk.rb +51 -0
- data/lib/mysql_genius/core/ai/rewrite_query.rb +51 -0
- data/lib/mysql_genius/core/ai/schema_context_builder.rb +82 -0
- data/lib/mysql_genius/core/ai/schema_review.rb +46 -0
- data/lib/mysql_genius/core/analysis/columns.rb +64 -0
- data/lib/mysql_genius/core/version.rb +1 -1
- data/lib/mysql_genius/core.rb +18 -0
- metadata +8 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 197d8fdf10094c1acf1487e9cc958e8a948f2faf16d0fc06d89193c1ccbc69c7
|
|
4
|
+
data.tar.gz: b2503519e572e9520fcda9c2e0adfd166cb68623d3a4fa1115034ff2e51ead0b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 22a75ed744368906927aada6311e76b4b8f4a2ae8dd8d6acbc8b7ac0486d1f7634b82f6cc0a1ef10324fa6984d191142d5e295210f0641896d654882536f9218
|
|
7
|
+
data.tar.gz: f3bbd7408ed91148f288cd95130158e4400c5c6d31e1b14527bd90fde64d2e211e6ff6e25c170847982ed19e73b9d9470c0a04d182a051e403ef71bf3ae2d235
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `MysqlGenius::Core::Analysis::Columns` — service class for `GET /columns` logic with a tagged-result struct (`:ok`, `:blocked`, `:not_found`). Takes a `Core::Connection`, uses `Core::SqlValidator.masked_column?` for the masked-column rule.
|
|
7
|
+
- `MysqlGenius::Core::Ai::{DescribeQuery, SchemaReview, RewriteQuery, IndexAdvisor, MigrationRisk}` — 5 AI prompt builder classes extracted from the `mysql_genius` Rails adapter's `AiFeatures` concern. Each takes `(client, config)` or `(client, config, connection)` and exposes a single `#call` method.
|
|
8
|
+
- `MysqlGenius::Core::Ai::SchemaContextBuilder` — shared helper for building "Table: X (~N rows), Columns: …, Indexes: …" schema descriptions. Supports `detail: :basic` and `detail: :with_cardinality`.
|
|
9
|
+
- `MysqlGenius::Core::Ai::Config#domain_context` — new optional keyword field (empty string default) interpolated into every extracted prompt builder's system prompt.
|
|
10
|
+
- `MysqlGenius::Core.views_path` — public module method returning the absolute path to the shared ERB template directory. Adapters register this path with their own view loader.
|
|
11
|
+
- **ERB templates extracted from the Rails adapter.** `MysqlGenius::Core.views_path` now points at `lib/mysql_genius/core/views/` which contains `mysql_genius/queries/dashboard.html.erb` and the 10 tab/partial files. Any adapter (Rails, Sinatra, or future desktop) can load these templates by registering this path with its own view loader. Templates depend on a minimal 2-method contract: `path_for(name)` and `render_partial(name)`.
|
|
12
|
+
|
|
3
13
|
## 0.4.1
|
|
4
14
|
|
|
5
15
|
No functional changes in `mysql_genius-core`. Version bumped to maintain lockstep with `mysql_genius 0.4.1`, which hotfixes a regression in the Rails adapter's `GET /columns` endpoint. See the root `CHANGELOG.md` for details.
|
|
@@ -16,6 +16,8 @@ module MysqlGenius
|
|
|
16
16
|
# auth_style - :bearer or :api_key
|
|
17
17
|
# system_context - optional domain context string that services
|
|
18
18
|
# append to their system prompts
|
|
19
|
+
# domain_context - optional host-app context string interpolated into
|
|
20
|
+
# AI system prompts (e.g. "Rails app, no FKs")
|
|
19
21
|
Config = Struct.new(
|
|
20
22
|
:client,
|
|
21
23
|
:endpoint,
|
|
@@ -23,10 +25,11 @@ module MysqlGenius
|
|
|
23
25
|
:model,
|
|
24
26
|
:auth_style,
|
|
25
27
|
:system_context,
|
|
28
|
+
:domain_context,
|
|
26
29
|
keyword_init: true,
|
|
27
30
|
) do
|
|
28
|
-
def initialize(
|
|
29
|
-
super
|
|
31
|
+
def initialize(**kwargs)
|
|
32
|
+
super(domain_context: "", **kwargs)
|
|
30
33
|
freeze
|
|
31
34
|
end
|
|
32
35
|
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MysqlGenius
|
|
4
|
+
module Core
|
|
5
|
+
module Ai
|
|
6
|
+
# Builds and sends a "describe this query" prompt to Core::Ai::Client.
|
|
7
|
+
# Pure function of SQL + config.domain_context — no connection lookup.
|
|
8
|
+
#
|
|
9
|
+
# Extracted from app/controllers/concerns/mysql_genius/ai_features.rb
|
|
10
|
+
# in Phase 2a.
|
|
11
|
+
class DescribeQuery
|
|
12
|
+
def initialize(client, config)
|
|
13
|
+
@client = client
|
|
14
|
+
@config = config
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def call(sql)
|
|
18
|
+
messages = [
|
|
19
|
+
{ role: "system", content: system_prompt },
|
|
20
|
+
{ role: "user", content: sql },
|
|
21
|
+
]
|
|
22
|
+
@client.chat(messages: messages)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def system_prompt
|
|
28
|
+
<<~PROMPT
|
|
29
|
+
You are a MySQL query explainer. Given a SQL query, explain in plain English:
|
|
30
|
+
1. What the query does (tables involved, joins, filters, aggregations)
|
|
31
|
+
2. How data flows through the query
|
|
32
|
+
3. Any subtle behaviors (implicit type casts, NULL handling in NOT IN, DISTINCT effects, etc.)
|
|
33
|
+
4. Potential performance concerns visible from the SQL structure alone
|
|
34
|
+
#{@config.domain_context}
|
|
35
|
+
Respond with JSON: {"explanation": "your plain-English explanation using markdown formatting"}
|
|
36
|
+
PROMPT
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MysqlGenius
|
|
4
|
+
module Core
|
|
5
|
+
module Ai
|
|
6
|
+
class IndexAdvisor
|
|
7
|
+
def initialize(client, config, connection)
|
|
8
|
+
@client = client
|
|
9
|
+
@config = config
|
|
10
|
+
@connection = connection
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def call(sql, explain_rows)
|
|
14
|
+
tables = SqlValidator.extract_table_references(sql, @connection)
|
|
15
|
+
schema = SchemaContextBuilder.new(@connection).call(tables, detail: :with_cardinality)
|
|
16
|
+
explain_text = explain_rows.map { |row| row.join(" | ") }.join("\n")
|
|
17
|
+
|
|
18
|
+
messages = [
|
|
19
|
+
{ role: "system", content: system_prompt },
|
|
20
|
+
{ role: "user", content: "Query:\n#{sql}\n\nEXPLAIN:\n#{explain_text}\n\nSchema:\n#{schema}" },
|
|
21
|
+
]
|
|
22
|
+
@client.chat(messages: messages)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def system_prompt
|
|
28
|
+
<<~PROMPT
|
|
29
|
+
You are a MySQL index advisor. Given a query, its EXPLAIN output, and current index/cardinality information, suggest optimal indexes. Consider:
|
|
30
|
+
- Composite index column ordering (most selective first, or matching query order)
|
|
31
|
+
- Covering indexes to avoid table lookups
|
|
32
|
+
- Partial indexes for long string columns
|
|
33
|
+
- Write-side costs (if this is a high-write table, note the INSERT/UPDATE overhead)
|
|
34
|
+
- Whether existing indexes could be extended rather than creating new ones
|
|
35
|
+
#{@config.domain_context}
|
|
36
|
+
|
|
37
|
+
Respond with JSON: {"indexes": "markdown-formatted recommendations with exact CREATE INDEX statements, rationale for column ordering, and estimated impact. Include any indexes that should be DROPPED as part of the change."}
|
|
38
|
+
PROMPT
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MysqlGenius
|
|
4
|
+
module Core
|
|
5
|
+
module Ai
|
|
6
|
+
class MigrationRisk
|
|
7
|
+
def initialize(client, config, connection)
|
|
8
|
+
@client = client
|
|
9
|
+
@config = config
|
|
10
|
+
@connection = connection
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def call(migration_sql)
|
|
14
|
+
tables = extract_table_names(migration_sql)
|
|
15
|
+
schema = SchemaContextBuilder.new(@connection).call(tables, detail: :basic)
|
|
16
|
+
schema_text = schema.to_s.empty? ? "Could not determine" : schema
|
|
17
|
+
|
|
18
|
+
messages = [
|
|
19
|
+
{ role: "system", content: system_prompt },
|
|
20
|
+
{ role: "user", content: "Migration:\n#{migration_sql}\n\nAffected Tables:\n#{schema_text}" },
|
|
21
|
+
]
|
|
22
|
+
@client.chat(messages: messages)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def extract_table_names(migration_sql)
|
|
28
|
+
# Match Rails migration helpers and raw SQL ALTER TABLE statements.
|
|
29
|
+
rails_matches = migration_sql.scan(/(?:create_table|add_column|remove_column|add_index|remove_index|rename_column|change_column|alter\s+table)\s+[:"]?(\w+)/i).flatten
|
|
30
|
+
sql_matches = migration_sql.scan(/ALTER\s+TABLE\s+`?(\w+)`?/i).flatten
|
|
31
|
+
(rails_matches + sql_matches).uniq
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def system_prompt
|
|
35
|
+
<<~PROMPT
|
|
36
|
+
You are a MySQL migration risk assessor. Given a Rails migration or DDL, evaluate:
|
|
37
|
+
1. Will this lock the table? For how long given the row count?
|
|
38
|
+
2. Is this safe to run during traffic, or does it need a maintenance window?
|
|
39
|
+
3. Should pt-online-schema-change or gh-ost be used instead?
|
|
40
|
+
4. Will it break or degrade any of the active queries against this table?
|
|
41
|
+
5. Are there any data loss risks?
|
|
42
|
+
6. What is the recommended deployment strategy?
|
|
43
|
+
#{@config.domain_context}
|
|
44
|
+
|
|
45
|
+
Respond with JSON: {"risk_level": "low|medium|high|critical", "assessment": "markdown-formatted risk assessment with specific recommendations and estimated lock duration"}
|
|
46
|
+
PROMPT
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MysqlGenius
|
|
4
|
+
module Core
|
|
5
|
+
module Ai
|
|
6
|
+
# Suggests a rewritten version of a SQL query based on the schema
|
|
7
|
+
# context of the tables it references.
|
|
8
|
+
class RewriteQuery
|
|
9
|
+
def initialize(client, config, connection)
|
|
10
|
+
@client = client
|
|
11
|
+
@config = config
|
|
12
|
+
@connection = connection
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def call(sql)
|
|
16
|
+
tables = SqlValidator.extract_table_references(sql, @connection)
|
|
17
|
+
schema = SchemaContextBuilder.new(@connection).call(tables, detail: :basic)
|
|
18
|
+
|
|
19
|
+
messages = [
|
|
20
|
+
{ role: "system", content: system_prompt(schema) },
|
|
21
|
+
{ role: "user", content: sql },
|
|
22
|
+
]
|
|
23
|
+
@client.chat(messages: messages)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def system_prompt(schema)
|
|
29
|
+
<<~PROMPT
|
|
30
|
+
You are a MySQL query rewrite expert. Analyze the SQL for anti-patterns and suggest a rewritten version. Look for:
|
|
31
|
+
- SELECT * when specific columns would suffice
|
|
32
|
+
- Correlated subqueries that could be JOINs
|
|
33
|
+
- OR conditions preventing index use (suggest UNION ALL)
|
|
34
|
+
- LIKE '%prefix' patterns (leading wildcard)
|
|
35
|
+
- Implicit type conversions in WHERE clauses
|
|
36
|
+
- NOT IN with NULLable columns (suggest NOT EXISTS)
|
|
37
|
+
- ORDER BY on non-indexed columns with LIMIT
|
|
38
|
+
- Unnecessary DISTINCT
|
|
39
|
+
- Functions on indexed columns in WHERE (e.g., DATE(created_at) instead of range)
|
|
40
|
+
|
|
41
|
+
Available schema:
|
|
42
|
+
#{schema}
|
|
43
|
+
#{@config.domain_context}
|
|
44
|
+
|
|
45
|
+
Respond with JSON: {"original": "the original SQL", "rewritten": "the improved SQL", "changes": "markdown list of each change and why it helps"}
|
|
46
|
+
PROMPT
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MysqlGenius
|
|
4
|
+
module Core
|
|
5
|
+
module Ai
|
|
6
|
+
# Builds formatted schema-description strings for AI prompt context.
|
|
7
|
+
# Used by SchemaReview, RewriteQuery, IndexAdvisor, and MigrationRisk.
|
|
8
|
+
#
|
|
9
|
+
# Consolidates the ~10 lines of schema description logic that were
|
|
10
|
+
# duplicated across 4 AI features in the Rails adapter's
|
|
11
|
+
# app/controllers/concerns/mysql_genius/ai_features.rb.
|
|
12
|
+
class SchemaContextBuilder
|
|
13
|
+
def initialize(connection)
|
|
14
|
+
@connection = connection
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Returns a formatted multi-line string describing the given tables.
|
|
18
|
+
#
|
|
19
|
+
# detail:
|
|
20
|
+
# :basic — name, row count, primary key, columns, indexes
|
|
21
|
+
# :with_cardinality — adds information_schema.STATISTICS cardinality per index
|
|
22
|
+
def call(tables, detail: :basic)
|
|
23
|
+
Array(tables).filter_map { |t| describe_table(t, detail: detail) }.join("\n\n")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def describe_table(table, detail:)
|
|
29
|
+
return unless @connection.tables.include?(table)
|
|
30
|
+
|
|
31
|
+
cols = @connection.columns_for(table).map do |c|
|
|
32
|
+
parts = ["#{c.name} #{c.sql_type}"]
|
|
33
|
+
parts << "NOT NULL" unless c.null
|
|
34
|
+
parts << "DEFAULT #{c.default}" if c.default
|
|
35
|
+
parts.join(" ")
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
pk = @connection.primary_key(table)
|
|
39
|
+
indexes = @connection.indexes_for(table).map do |idx|
|
|
40
|
+
"#{"UNIQUE " if idx.unique}INDEX #{idx.name} (#{idx.columns.join(", ")})"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
row_count = fetch_row_count(table)
|
|
44
|
+
|
|
45
|
+
parts = [
|
|
46
|
+
"Table: #{table} (~#{row_count || "unknown"} rows)",
|
|
47
|
+
"Primary Key: #{pk || "NONE"}",
|
|
48
|
+
"Columns: #{cols.join(", ")}",
|
|
49
|
+
"Indexes: #{indexes.any? ? indexes.join(", ") : "NONE"}",
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
parts << index_cardinality(table) if detail == :with_cardinality
|
|
53
|
+
|
|
54
|
+
parts.join("\n")
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def fetch_row_count(table)
|
|
58
|
+
sql = "SELECT TABLE_ROWS FROM information_schema.tables " \
|
|
59
|
+
"WHERE table_schema = #{@connection.quote(@connection.current_database)} " \
|
|
60
|
+
"AND table_name = #{@connection.quote(table)}"
|
|
61
|
+
result = @connection.exec_query(sql)
|
|
62
|
+
result.rows.first&.first
|
|
63
|
+
rescue StandardError
|
|
64
|
+
nil
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def index_cardinality(table)
|
|
68
|
+
sql = "SELECT INDEX_NAME, COLUMN_NAME, CARDINALITY, SEQ_IN_INDEX " \
|
|
69
|
+
"FROM information_schema.STATISTICS " \
|
|
70
|
+
"WHERE TABLE_SCHEMA = #{@connection.quote(@connection.current_database)} " \
|
|
71
|
+
"AND TABLE_NAME = #{@connection.quote(table)} " \
|
|
72
|
+
"ORDER BY INDEX_NAME, SEQ_IN_INDEX"
|
|
73
|
+
result = @connection.exec_query(sql)
|
|
74
|
+
stats = result.rows.map { |r| "#{r[0]}.#{r[1]}: cardinality=#{r[2]}" }
|
|
75
|
+
"Cardinality: #{stats.join(", ")}"
|
|
76
|
+
rescue StandardError
|
|
77
|
+
"Cardinality: (unavailable)"
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MysqlGenius
|
|
4
|
+
module Core
|
|
5
|
+
module Ai
|
|
6
|
+
# Reviews a schema for anti-patterns. Takes a specific table name
|
|
7
|
+
# (or nil to review the top 20 queryable tables).
|
|
8
|
+
class SchemaReview
|
|
9
|
+
def initialize(client, config, connection)
|
|
10
|
+
@client = client
|
|
11
|
+
@config = config
|
|
12
|
+
@connection = connection
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def call(table)
|
|
16
|
+
tables_to_review = table.nil? || table.to_s.empty? ? @connection.tables.first(20) : [table]
|
|
17
|
+
schema_desc = SchemaContextBuilder.new(@connection).call(tables_to_review, detail: :basic)
|
|
18
|
+
|
|
19
|
+
messages = [
|
|
20
|
+
{ role: "system", content: system_prompt },
|
|
21
|
+
{ role: "user", content: schema_desc },
|
|
22
|
+
]
|
|
23
|
+
@client.chat(messages: messages)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def system_prompt
|
|
29
|
+
<<~PROMPT
|
|
30
|
+
You are a MySQL schema reviewer. Analyze the following schema and identify anti-patterns and improvement opportunities. Look for:
|
|
31
|
+
- Inappropriate column types (VARCHAR(255) for short values, TEXT where VARCHAR suffices, INT for booleans)
|
|
32
|
+
- Missing indexes on foreign key columns or frequently filtered columns
|
|
33
|
+
- Missing NOT NULL constraints where NULLs are unlikely
|
|
34
|
+
- ENUM columns that should be lookup tables
|
|
35
|
+
- Missing created_at/updated_at timestamps
|
|
36
|
+
- Tables without a PRIMARY KEY
|
|
37
|
+
- Overly wide indexes or redundant indexes
|
|
38
|
+
- Column naming inconsistencies
|
|
39
|
+
#{@config.domain_context}
|
|
40
|
+
Respond with JSON: {"findings": "markdown-formatted findings organized by severity (Critical, Warning, Suggestion). Include specific ALTER TABLE statements where applicable."}
|
|
41
|
+
PROMPT
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MysqlGenius
|
|
4
|
+
module Core
|
|
5
|
+
module Analysis
|
|
6
|
+
# Service class for the Rails engine's GET /columns action and its
|
|
7
|
+
# Phase 2b sidecar equivalent. Takes a Core::Connection and the
|
|
8
|
+
# relevant configuration; returns a tagged Result struct that
|
|
9
|
+
# adapters map to HTTP responses.
|
|
10
|
+
#
|
|
11
|
+
# Each status maps 1:1 to an HTTP status code:
|
|
12
|
+
# :ok → 200 with columns: array
|
|
13
|
+
# :blocked → 403 with error_message:
|
|
14
|
+
# :not_found → 404 with error_message:
|
|
15
|
+
#
|
|
16
|
+
# The adapter reads result.status and dispatches accordingly.
|
|
17
|
+
class Columns
|
|
18
|
+
Result = Struct.new(:status, :columns, :error_message, keyword_init: true)
|
|
19
|
+
|
|
20
|
+
def initialize(connection, blocked_tables:, masked_column_patterns:, default_columns:)
|
|
21
|
+
@connection = connection
|
|
22
|
+
@blocked_tables = blocked_tables
|
|
23
|
+
@masked_column_patterns = masked_column_patterns
|
|
24
|
+
@default_columns = default_columns
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def call(table:)
|
|
28
|
+
return blocked_result(table) if @blocked_tables.include?(table)
|
|
29
|
+
return not_found_result(table) unless @connection.tables.include?(table)
|
|
30
|
+
|
|
31
|
+
defaults = @default_columns[table] || []
|
|
32
|
+
visible = @connection.columns_for(table).reject do |col|
|
|
33
|
+
SqlValidator.masked_column?(col.name, @masked_column_patterns)
|
|
34
|
+
end
|
|
35
|
+
formatted = visible.map do |col|
|
|
36
|
+
{
|
|
37
|
+
name: col.name,
|
|
38
|
+
type: col.type.to_s,
|
|
39
|
+
default: defaults.empty? || defaults.include?(col.name),
|
|
40
|
+
}
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
Result.new(status: :ok, columns: formatted)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def blocked_result(table)
|
|
49
|
+
Result.new(
|
|
50
|
+
status: :blocked,
|
|
51
|
+
error_message: "Table '#{table}' is not available for querying.",
|
|
52
|
+
)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def not_found_result(table)
|
|
56
|
+
Result.new(
|
|
57
|
+
status: :not_found,
|
|
58
|
+
error_message: "Table '#{table}' does not exist.",
|
|
59
|
+
)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
data/lib/mysql_genius/core.rb
CHANGED
|
@@ -10,6 +10,17 @@ module MysqlGenius
|
|
|
10
10
|
# overall design.
|
|
11
11
|
module Core
|
|
12
12
|
class Error < StandardError; end
|
|
13
|
+
|
|
14
|
+
class << self
|
|
15
|
+
# Absolute path to the shared ERB template directory. Adapters
|
|
16
|
+
# register this path with their view loader:
|
|
17
|
+
#
|
|
18
|
+
# Rails: engine.config.paths["app/views"] << MysqlGenius::Core.views_path
|
|
19
|
+
# Sinatra: set :views, MysqlGenius::Core.views_path
|
|
20
|
+
def views_path
|
|
21
|
+
File.expand_path("core/views", __dir__)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
13
24
|
end
|
|
14
25
|
end
|
|
15
26
|
|
|
@@ -24,11 +35,18 @@ require "mysql_genius/core/ai/config"
|
|
|
24
35
|
require "mysql_genius/core/ai/client"
|
|
25
36
|
require "mysql_genius/core/ai/suggestion"
|
|
26
37
|
require "mysql_genius/core/ai/optimization"
|
|
38
|
+
require "mysql_genius/core/ai/schema_context_builder"
|
|
39
|
+
require "mysql_genius/core/ai/describe_query"
|
|
40
|
+
require "mysql_genius/core/ai/schema_review"
|
|
41
|
+
require "mysql_genius/core/ai/rewrite_query"
|
|
42
|
+
require "mysql_genius/core/ai/index_advisor"
|
|
43
|
+
require "mysql_genius/core/ai/migration_risk"
|
|
27
44
|
require "mysql_genius/core/analysis/table_sizes"
|
|
28
45
|
require "mysql_genius/core/analysis/duplicate_indexes"
|
|
29
46
|
require "mysql_genius/core/analysis/query_stats"
|
|
30
47
|
require "mysql_genius/core/analysis/unused_indexes"
|
|
31
48
|
require "mysql_genius/core/analysis/server_overview"
|
|
49
|
+
require "mysql_genius/core/analysis/columns"
|
|
32
50
|
require "mysql_genius/core/execution_result"
|
|
33
51
|
require "mysql_genius/core/query_runner/config"
|
|
34
52
|
require "mysql_genius/core/query_runner"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mysql_genius-core
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Antarr Byrd
|
|
@@ -24,8 +24,15 @@ files:
|
|
|
24
24
|
- lib/mysql_genius/core.rb
|
|
25
25
|
- lib/mysql_genius/core/ai/client.rb
|
|
26
26
|
- lib/mysql_genius/core/ai/config.rb
|
|
27
|
+
- lib/mysql_genius/core/ai/describe_query.rb
|
|
28
|
+
- lib/mysql_genius/core/ai/index_advisor.rb
|
|
29
|
+
- lib/mysql_genius/core/ai/migration_risk.rb
|
|
27
30
|
- lib/mysql_genius/core/ai/optimization.rb
|
|
31
|
+
- lib/mysql_genius/core/ai/rewrite_query.rb
|
|
32
|
+
- lib/mysql_genius/core/ai/schema_context_builder.rb
|
|
33
|
+
- lib/mysql_genius/core/ai/schema_review.rb
|
|
28
34
|
- lib/mysql_genius/core/ai/suggestion.rb
|
|
35
|
+
- lib/mysql_genius/core/analysis/columns.rb
|
|
29
36
|
- lib/mysql_genius/core/analysis/duplicate_indexes.rb
|
|
30
37
|
- lib/mysql_genius/core/analysis/query_stats.rb
|
|
31
38
|
- lib/mysql_genius/core/analysis/server_overview.rb
|