SQLite to Markdown

approved

by Michael Collard

Render SQLite queries as markdown tables via fenced code blocks. - This plugin has not been manually reviewed by Obsidian staff.

โ†“ 33 downloadsUpdated 9d agoMIT

SQLite to Markdown

An Obsidian plugin that runs SQLite queries against local databases and renders the results as tables in reading view. Write SQL in a fenced code block, get a live table.

Usage

Add a fenced sql (or sqlite) code block to any note:

```sql
db: /var/lib/jellyfin/data/jellyfin.db
SELECT name, department, salary
FROM employees
WHERE department = 'Engineering'
ORDER BY salary DESC;
```

Or use an ODBC-style connection string with an ObsSync block:

```ObsSync
DRIVER=sqlite;DATABASE=/var/lib/jellyfin/data/jellyfin.db;QUERY=SELECT name, department, salary FROM employees WHERE department = 'Engineering' ORDER BY salary DESC
```

Switch to reading view โ€” the code block is replaced with a rendered table:

namedepartmentsalary
Eve JohnsonEngineering140000
Carol DavisEngineering130000
Alice ChenEngineering125000
Hank BrownEngineering115000

A footer row shows the database filename, when the data was retrieved, and the row count. Click ๐Ÿ“‚ or ๐Ÿ”„ in the footer to manually refresh.

Block Formats

sql / sqlite

db: /absolute/path/to/database.db
SELECT ...;
  • db: โ€” path to the SQLite database file. Absolute paths or vault-relative paths.
  • Query โ€” any read-only SQL: SELECT, WITH ... SELECT, or EXPLAIN QUERY PLAN.
  • Write operations (INSERT, UPDATE, DELETE, DROP, etc.) are blocked.

ObsSync (ODBC-style)

DRIVER=sqlite;DATABASE=/absolute/path/to/database.db;QUERY=SELECT ...
  • DRIVER= โ€” optional, defaults to sqlite. Currently only sqlite is supported.
  • DATABASE= โ€” path to the SQLite database file. Absolute paths or vault-relative paths.
  • QUERY= โ€” must be last; its value extends to end of the block, so semicolons inside the SQL are safe.

Features

  • Live query โ€” reads the database from disk each time the note is rendered (desktop).
  • Cached fallback โ€” after a successful query, results are cached inside the code block. If the database is unavailable, the cached result is shown with a โš ๏ธ cached indicator.
  • Mobile support โ€” on mobile, cached data is displayed read-only. Open on desktop first to populate the cache.
  • Refresh modes โ€” control when queries re-run via the REFRESH: directive (see below).
  • Timed auto-refresh โ€” REFRESH:15m re-runs the query every N time units while the note is open.
  • Interactive footer โ€” ๐Ÿ“‚ and ๐Ÿ”„ in the footer row are clickable; they trigger a live refresh on desktop or show a notice on mobile.
  • Copy as Markdown โ€” button below each table copies the result as a pipe-delimited markdown table.
  • Index support โ€” uses sql.js (SQLite compiled to WebAssembly), so all SQLite features work including indexes, joins, CTEs, and window functions.
  • Read-only by design โ€” the database is loaded into memory as a read-only snapshot. No changes are ever written back.
  • Instructional messages โ€” clear guidance instead of cryptic errors when something is misconfigured.

Refresh Modes

Add a /* REFRESH:... */ comment anywhere in the block to control when the query runs:

ValueBehaviour
(omitted)Auto โ€” query runs on every render (default)
REFRESH:AUTOSame as default
REFRESH:MANUALShow cache on render; live query only on ๐Ÿ”„ click
REFRESH:15mAuto on render + re-query every 15 minutes

Time units: ms ยท s / sec(s) / second(s) ยท m / min(s) / minute(s) ยท h / hr(s) / hour(s) ยท d / day(s). Decimal values (1.5h) are valid.

Examples:

```sql
db: /var/lib/jellyfin/data/jellyfin.db
SELECT Type, count(*) FROM BaseItems GROUP BY Type;
/* REFRESH:5m */
```
```sql
db: /path/to/large.db
SELECT * FROM expensive_view;
/* REFRESH:MANUAL */
```

How Caching Works

After a successful query, the plugin writes a /* REFRESH:... | CACHE:{...} */ comment to the end of the code block. This is invisible in reading view but preserved in the markdown source:

```sql
db: /var/lib/jellyfin/data/jellyfin.db
SELECT Type, count(*) FROM BaseItems GROUP BY Type;
/* REFRESH:5m | CACHE:{"columns":["Type","count(*)"],"rows":[...],"dbName":"jellyfin.db","timestamp":"2026-06-08 14:00:00","rowCount":7} */
```

The cache is updated on each successful query. When the database is unreachable, the last cached result is displayed. If no REFRESH: directive is present, the comment is just /* CACHE:{...} */ (backward compatible).

Installation

From source

npm install
npm run build

Copy main.js, manifest.json, and styles.css into your vault:

.obsidian/plugins/obs-sqlite-md/
โ”œโ”€โ”€ main.js
โ”œโ”€โ”€ manifest.json
โ””โ”€โ”€ styles.css

Enable the plugin in Settings โ†’ Community plugins.

Development

npm run dev    # watch mode โ€” rebuilds on file changes

Requirements

  • Desktop โ€” live queries use Node.js fs to read database files from disk. Absolute paths and vault-relative paths both work.
  • Mobile โ€” displays cached data only. Open the note on desktop first to populate the cache; subsequent mobile views show the last cached result.
  • Databases can live anywhere the desktop OS user has read access (inside or outside the vault).

Technical Notes

  • Uses sql.js v1.14+ โ€” SQLite compiled to WebAssembly via Emscripten.
  • The WASM binary (~644 KB) is embedded into main.js at build time. No separate .wasm file to manage.
  • Databases are read into memory as a Uint8Array. Large databases (hundreds of MB) will use proportional memory.
  • Registers sql, sqlite, and ObsSync as code block languages.
  • Node.js fs is required dynamically at runtime (never at module load), so the plugin loads safely on mobile without crashing.
  • Timed refresh uses a window.setTimeout chain; the chain stops automatically when el.isConnected is false (note closed or navigated away).
  • See ARCHITECTURE.md for full design details.

License

MIT

For plugin developers

Search results and similarity scores are powered by semantic analysis of your plugin's README. If your plugin isn't appearing for searches you'd expect, try updating your README to clearly describe your plugin's purpose, features, and use cases.