SQL for Markdown Tables
Warning: This is an experimental program that modifies files in-place. Always use version control (git) or back up your markdown files before running INSERT or DELETE operations.
mdsql treats tables in markdown files as a lightweight database. Query, insert, and delete rows using familiar SQL syntax while keeping your data human-readable.
The idea is simple: markdown tables are already structured data. Why not query them like a database? This gives you the best of both worlds, files you can read and edit by hand, with the power of SQL when you need it. The SQL itself never includes filesystem paths; the file stays a CLI argument.
- Query markdown tables with SELECT, WHERE, ORDER BY, LIMIT
- Insert, update, and delete rows with standard SQL syntax
- Output as columns, markdown, CSV, JSON, or TSV
- Unix-friendly: default output pipes well with other tools
- Tables addressed by 0-based index
Install
Install with Cargo:
cargo install mdsql
Quick Start
Given a markdown file with a table:
# Team
| name | role | level |
|---------|-----------|-------|
| Alice | Engineer | 3 |
| Bob | Designer | 2 |
| Charlie | Manager | 4 |
List tables in a file:
$ mdsql tables team.md
Table 0: 3 columns, 3 rows
Columns: name, role, level
Query with SQL:
$ mdsql query "SELECT name, role FROM 0 WHERE level > 2" team.md
name role
Alice Engineer
Charlie Manager
Supported SQL
SELECT
# Select all columns
mdsql query "SELECT * FROM 0" data.md
# Select specific columns
mdsql query "SELECT name, age FROM 0" data.md
# Filter with WHERE
mdsql query "SELECT * FROM 0 WHERE status = 'active'" data.md
# Sort results
mdsql query "SELECT * FROM 0 ORDER BY score DESC" data.md
# Limit results
mdsql query "SELECT * FROM 0 LIMIT 10" data.md
# Combine clauses
mdsql query "SELECT name, score FROM 0 WHERE score > 50 ORDER BY score DESC LIMIT 5" data.md
INSERT
# Insert a new row
mdsql insert "INSERT INTO 0 (name, role, level) VALUES ('Diana', 'Engineer', '2')" team.md
DELETE
# Delete matching rows
mdsql delete "DELETE FROM 0 WHERE name = 'Bob'" team.md
# Delete all rows (use with caution)
mdsql delete "DELETE FROM 0" team.md
UPDATE
# Update matching rows
mdsql update "UPDATE 0 SET role = 'Lead' WHERE name = 'Alice'" team.md
Output Formats
Use --format to change output:
# Column-aligned (default, like df -h)
mdsql query "SELECT * FROM 0" data.md
# Markdown table
mdsql query --format markdown "SELECT * FROM 0" data.md
# CSV
mdsql query --format csv "SELECT * FROM 0" data.md
# JSON
mdsql query --format json "SELECT * FROM 0" data.md
# Tab-separated
mdsql query --format tsv "SELECT * FROM 0" data.md
Unix Pipelines
The default column output works well with standard Unix tools:
# Count rows matching a condition
mdsql query "SELECT * FROM 0 WHERE status = 'active'" data.md | tail -n +2 | wc -l
# Filter output with grep
mdsql query "SELECT * FROM 0" data.md | grep "Engineer"
# Process with awk
mdsql query "SELECT name, score FROM 0" data.md | awk 'NR>1 {sum+=$2} END {print sum}'
Table Addressing
Tables are addressed by their 0-based index in the document. The first table
is FROM 0, the second is FROM 1, and so on.
# Query the first table
mdsql query "SELECT * FROM 0" data.md
# Query the second table
mdsql query "SELECT * FROM 1" data.md
Roadmap
- Split into library crate and CLI crate
- FFI-first library for multiple languages (Python first)
- Aggregate functions (SUM, COUNT, AVG, MIN, MAX)
- GROUP BY
- Infer table names from preceding markdown headers