← Back to articles

Run your SQL files with sqlrunner

I interact with PostgreSQL databases using psql, a basic but configurable tool that is enough for most of the work I have to do. I also manage a collection of SQL files in order to display reports, troubleshoot things on production systems, or many other usages.

These files use psql variables, set from the command line with -v:

$ cat /tmp/foo.sql
select :'foo';

$ psql -At -f /tmp/foo.sql
psql:/tmp/foo.sql:1: ERROR:  syntax error at or near ":"
LINE 1: select :'foo';

$ psql -At -f /tmp/foo.sql -v foo=bar
bar

I needed something to easily manage this collection of files, being able to list all of them with a description, as well as the list of variables they support. So I built sqlrunner, using Rust and 100% LLM-agent engineering. It works on top of psql.

sqlrunner examples

In addition to the README, here are some examples of the tool usage.

Listing, over the example/ directory of the repository (load example.schema.sql first):

$ sqlrunner --sql-dir=example
FILE        VARIABLES                      DESCRIPTION
audit.sql   action, row_limit, table_name  Audit trail of a table, ignoring quoted lookalikes
orders.sql  end_date, start_date, status   Orders of a period, by status
stats.sql                                  User counts, total and last 30 days
users.sql   user_id                        Details of one user

Setting config options as environment variables, then running a file:

$ export SQLRUNNER_SQL_DIR=example
$ export SQLRUNNER_DSN=postgresql://user@localhost/sqlrunner
$ sqlrunner stats.sql
psql --quiet \
    -d postgresql://user@localhost/sqlrunner \
    -f example/stats.sql

 users │ recent
═══════╪════════
     3 │      2
(1 row)

Query with variable:

$ sqlrunner orders.sql status='in progress' start_date=2026-01-01 end_date=2026-02-01
psql --quiet \
    -d postgresql://user@localhost/sqlrunner \
    -v end_date=2026-02-01 \
    -v start_date=2026-01-01 \
    -v 'status=in progress' \
    -f example/orders.sql

 id │   status    │ total
════╪═════════════╪════════
  1 │ in progress │  42.00
  3 │ in progress │ 128.90
(2 rows)

Query with variable interactively set:

$ export SQLRUNNER_INTERACTIVE=true
$ sqlrunner orders.sql
end_date: 2026-02-01
start_date: 2026-01-01
status: in progress
psql --quiet \
    -d postgresql://user@localhost/sqlrunner \
...

Also, sqlrunner supports completion of filenames and variables, which can be very time-saving for a hardcore user like me!

What was the development process?

I first started with a fresh git init and a simple CLAUDE.md. Note that I also have a personal CLAUDE.md with general rules like “Always add unit tests”. For this project I made the experiment of keeping a history of the prompts I wrote in a specific file: PROMPTS.md.

I always use a verb at the beginning of these prompts and keep them single line. In the past I would have been worried about the quality of the code I wrote. Now I worry about the English of these prompts I share with you!

The commits have been the minimal unit of work for this project. I discarded or rewrote some of these, but most were fine. When Claude was working to implement one of the prompts, I was busy writing the next ones to keep the software factory smooth :)