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
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pksql-0.2.0.tar.gz.
File metadata
- Download URL: pksql-0.2.0.tar.gz
- Upload date:
- Size: 16.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
04ddd87295ebff3c2b506c044a6f89cee059bcf0fb104ec110a0fd17dd681c6f
|
|
| MD5 |
775bc1503d0fb3a63cf706925948c3b7
|
|
| BLAKE2b-256 |
432728800790db203572c041d503b9956b75aabf5ae82989714a5efb9c3ab7b1
|
Provenance
The following attestation bundles were made for pksql-0.2.0.tar.gz:
Publisher:
publish.yml on dbolser/pksql
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pksql-0.2.0.tar.gz -
Subject digest:
04ddd87295ebff3c2b506c044a6f89cee059bcf0fb104ec110a0fd17dd681c6f - Sigstore transparency entry: 2548469985
- Sigstore integration time:
-
Permalink:
dbolser/pksql@0261d4091cf803429292a69d950d2fc1e6895b45 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/dbolser
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@0261d4091cf803429292a69d950d2fc1e6895b45 -
Trigger Event:
release
-
Statement type:
File details
Details for the file pksql-0.2.0-py3-none-any.whl.
File metadata
- Download URL: pksql-0.2.0-py3-none-any.whl
- Upload date:
- Size: 11.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4e94c26f4a5a6596de64128467ad3d5c9a50573fea989bf7f37f3935b42cd6ac
|
|
| MD5 |
b953022423fc10a87a9197d89c32644d
|
|
| BLAKE2b-256 |
81de49a18c6fda130853cb4a90fd9c300ff47770dfa1a010808343e7b6f92047
|
Provenance
The following attestation bundles were made for pksql-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on dbolser/pksql
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pksql-0.2.0-py3-none-any.whl -
Subject digest:
4e94c26f4a5a6596de64128467ad3d5c9a50573fea989bf7f37f3935b42cd6ac - Sigstore transparency entry: 2548470267
- Sigstore integration time:
-
Permalink:
dbolser/pksql@0261d4091cf803429292a69d950d2fc1e6895b45 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/dbolser
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@0261d4091cf803429292a69d950d2fc1e6895b45 -
Trigger Event:
release
-
Statement type: