Skip to content

Instantly share code, notes, and snippets.

@eduardoarandah
Last active April 15, 2026 18:18
Show Gist options
  • Select an option

  • Save eduardoarandah/fbf0c0f82160d0a3006bb0bfc3e16a66 to your computer and use it in GitHub Desktop.

Select an option

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