Skip to main content

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'
  • ~/.pksql applies everywhere; ./.pksql adds to it and wins on a name clash.
  • Relative paths are read relative to the .pksql file, 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 aliases marks those (missing).
  • An alias named after a DuckDB keyword works, but the query has to quote it: pksql 'SELECT * FROM "select"'. add-alias says 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-url line 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)

Source distribution for pksql 0.2.0
File Size Uploaded
pksql-0.2.0.tar.gz 16.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pksql 0.2.0
File Interpreter ABI Platform
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 log

Release 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

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page