pksql
Command line SQL on parquet files using DuckDB.
pksql runs a DuckDB query from your shell and prints the result. Aliases let you
give a long path a short name once, in a .pksql file, instead of retyping it.
Installation
For Users
Install directly from GitHub:
# Using pip
pip install git+https://github.com/dbolser/pksql.git
# Using pip with SSH (if you have SSH keys configured)
pip install git+ssh://git@github.com/dbolser/pksql.git
For Developers
pip install -e .
# or
uv pip install -e .
Usage
# Query a single file
pksql "SELECT * FROM 'data.parquet'"
# Query many at once
pksql "SELECT COUNT(*) FROM 'multiple_*.parquet'"
# CSVs work the same way
pksql "SELECT * FROM 'data.csv'"
Quote the query. Otherwise your shell expands * before pksql sees it.
A .duckdb file can be read the same way, but only if it holds exactly one
table — otherwise DuckDB says Database "corpus.duckdb" has multiple tables.
For those, attach it and name the table:
pksql "SELECT * FROM 'corpus.duckdb'"
pksql "ATTACH 'corpus.duckdb' AS c; SELECT * FROM c.documents"
Aliases
Give a path a name, and use that name as a table:
pksql add-alias corpus = data/s3-backup-20260731/karl/corpus.duckdb
pksql "SELECT * FROM corpus"
add-alias writes to .pksql in the current directory. The = is optional, so
pksql add-alias corpus data/corpus.duckdb does the same thing.
# A glob works too - quote it so the shell leaves it alone
pksql add-alias hits 'results/*.parquet'
# Forget the quotes and your shell expands it first; pksql says so
# Warning: that is 3 files, not one path - your shell expanded the glob.
# Available everywhere, not just this directory
pksql add-alias --global scratch ~/scratch.duckdb
# What's registered, and where from
pksql aliases
# Forget one
pksql rm-alias corpus
The .pksql file
It is a plain list of name = path lines, so you can edit it by hand:
# Karl's backup, 2026-07-31
corpus = data/s3-backup-20260731/karl/corpus.duckdb
hits = 'results/*.parquet'
~/.pksqlapplies everywhere;./.pksqladds to it and wins on a name clash.- Relative paths are read relative to the
.pksqlfile, not to where you are. - An alias pointing at something that isn't there is ignored, so an unplugged
drive breaks only the queries that actually name it.
pksql aliasesmarks those(missing). - An alias named after a DuckDB keyword works, but the query has to quote it:
pksql 'SELECT * FROM "select"'.add-aliassays so when you register one.
Output formats
--output-format (-F) takes table (default), csv, tsv or json:
pksql -F json "SELECT * FROM corpus" | jq .
Results go to stdout; the query time and any errors go to stderr, so piping stays clean.
Requirements
- Python 3.10+
- DuckDB, Click, Rich
Project History
This project started with a simple idea:
I want a simple 'command line' utility that lets me run DuckDB SQL on a given set of parquet files.
It briefly grew an interactive REPL. That turned out to be the wrong shape — the
aliases you set up there died with the session, so they were never worth
registering. Persisting them to a .pksql file gave the one-shot CLI the same
convenience, and the REPL was dropped.
TODO
- Publish to PyPI (TestPyPI is wired up via trusted publishing; real PyPI
needs its own publisher and the
repository-urlline dropped) - Add schema inspection commands
- Support for saving query results to files
Metadata
Release files for pksql 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pksql-0.2.0.tar.gz | 16.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pksql-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 27.9 kB
Release files / pksql-0.2.0.tar.gz
| Download URL | pksql-0.2.0.tar.gz |
|---|---|
| Size | 16.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
04ddd87295ebff3c2b506c044a6f89cee059bcf0fb104ec110a0fd17dd681c6f
|
|
BLAKE2b-256 checksum How to use checksums |
432728800790db203572c041d503b9956b75aabf5ae82989714a5efb9c3ab7b1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 21, 2026.
Transparency logRelease files / pksql-0.2.0-py3-none-any.whl
| Download URL | pksql-0.2.0-py3-none-any.whl |
|---|---|
| Size | 11.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4e94c26f4a5a6596de64128467ad3d5c9a50573fea989bf7f37f3935b42cd6ac
|
|
BLAKE2b-256 checksum How to use checksums |
81de49a18c6fda130853cb4a90fd9c300ff47770dfa1a010808343e7b6f92047
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 21, 2026.
Transparency log