Last active
April 15, 2026 18:18
-
-
Save eduardoarandah/fbf0c0f82160d0a3006bb0bfc3e16a66 to your computer and use it in GitHub Desktop.
I made a quick and dirty plugin to describe my database so that I can send it to the LLM it has 3 commands: SQLDefine SQLTables SQLExecute
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| -- The objective is passing one or more table names and getting a SINGLE formatted markdown code block | |
| -- | |
| -- Commands: | |
| -- SQLDescribe: Describe table(s) from current line or range → one ```sql block | |
| -- SQLTables: Show all tables in the database | |
| -- | |
| -- Requires an .env file with: | |
| -- DB_CONNECTION=mysql | |
| -- DB_HOST=localhost | |
| -- DB_PORT=3306 | |
| -- DB_DATABASE=example | |
| -- DB_USERNAME=asdf | |
| -- DB_PASSWORD=asdf | |
| local M = {} | |
| -- Execute SQL and return clean results (filters only warnings) | |
| local function execute_sql(sql_statements) | |
| -- Find .env file in project hierarchy | |
| local env_file = vim.fn.findfile(".env", ".;") | |
| if env_file == "" then | |
| print("Error: .env file not found in project hierarchy") | |
| return nil | |
| end | |
| local all_results = {} | |
| for _, sql in ipairs(sql_statements) do | |
| -- Use docker compose + mariadb | |
| local command = 'source .env && docker compose exec -it db mariadb -u $DB_USERNAME -p$DB_PASSWORD -h $DB_HOST -P $DB_PORT $DB_DATABASE -e "' .. sql .. '"' | |
| local lines = vim.fn.systemlist(command) | |
| -- Remove only warning lines | |
| lines = vim.tbl_filter(function(line) | |
| return not line:match("%[Warning%]") | |
| end, lines) | |
| -- Add every result line | |
| for _, line in ipairs(lines) do | |
| table.insert(all_results, line) | |
| end | |
| end | |
| return all_results | |
| end | |
| -- SQLDescribe: ONE single ```sql block with nice blank lines for readability | |
| local function sql_describe(opts) | |
| -- Get range (works for single line or visual/range selection) | |
| local line1 = opts and opts.line1 or vim.fn.line(".") | |
| local line2 = opts and opts.line2 or vim.fn.line(".") | |
| -- Read all selected lines | |
| local buf_lines = vim.api.nvim_buf_get_lines(0, line1 - 1, line2, false) | |
| -- Extract valid table names (one word per line) | |
| local table_names = {} | |
| for _, line in ipairs(buf_lines) do | |
| local trimmed = vim.fn.trim(line) | |
| if trimmed ~= "" then | |
| local words = vim.fn.split(trimmed, "\\s\\+") | |
| if #words == 1 then | |
| table.insert(table_names, words[1]) | |
| else | |
| print("Warning: Skipping line (must have exactly one word): " .. trimmed) | |
| end | |
| end | |
| end | |
| if #table_names == 0 then | |
| print("Error: No valid table names found in the selected range") | |
| return | |
| end | |
| -- Build ONE single markdown block | |
| local formatted = { "```sql" } | |
| for i, table_name in ipairs(table_names) do | |
| -- Run the two queries | |
| local describe_results = execute_sql({ "DESCRIBE " .. table_name .. ";" }) | |
| local index_results = execute_sql({ "SHOW INDEX FROM " .. table_name .. ";" }) | |
| -- === DESCRIBE section === | |
| table.insert(formatted, "DESCRIBE " .. table_name .. ";") | |
| table.insert(formatted, "") -- ← blank line between statement and results | |
| for _, line in ipairs(describe_results or {}) do | |
| table.insert(formatted, line) | |
| end | |
| table.insert(formatted, "") -- ← blank line after results (separates from next statement) | |
| -- === SHOW INDEX section === | |
| table.insert(formatted, "SHOW INDEX FROM " .. table_name .. ";") | |
| table.insert(formatted, "") -- ← blank line between statement and results | |
| for _, line in ipairs(index_results or {}) do | |
| table.insert(formatted, line) | |
| end | |
| -- === Separator between tables (only if there are more tables) === | |
| if i < #table_names then | |
| table.insert(formatted, "") -- blank line after last results | |
| table.insert(formatted, "--") | |
| table.insert(formatted, "") -- blank line after separator | |
| end | |
| end | |
| -- Close the code block | |
| table.insert(formatted, "```") | |
| -- Replace the whole selected range with the new single block | |
| vim.api.nvim_buf_set_lines(0, line1 - 1, line2, false, formatted) | |
| end | |
| -- Show all tables (unchanged) | |
| local function sql_tables() | |
| local results = execute_sql({ "SHOW TABLES;" }) | |
| if results then | |
| if #results > 0 and results[1]:match("^Tables_in_") then | |
| local db_name = results[1]:match("^Tables_in_(.+)$") | |
| results[1] = "## Tables in database " .. db_name | |
| table.insert(results, 2, "") | |
| end | |
| local line_num = vim.fn.line(".") | |
| vim.api.nvim_buf_set_lines(0, line_num, line_num, false, results) | |
| end | |
| end | |
| function M.setup() | |
| -- SQLDescribe now supports ranges and produces a single clean block | |
| vim.api.nvim_create_user_command("SQLDescribe", function(opts) | |
| sql_describe(opts) | |
| end, { | |
| desc = "Describe one or more tables → single ```sql block", | |
| range = true, | |
| }) | |
| vim.api.nvim_create_user_command("SQLTables", function() | |
| sql_tables() | |
| end, { desc = "Show all tables in the database" }) | |
| end | |
| return M |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment