timescale / timescale/tiger-cli
tiger db: no way to load a local file (no COPY ... FROM STDIN equivalent)
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 119
- Forks
- 12
- Avg merge
- 2d 15h
- Merged PRs (30d)
- 33
Description
Summary
There's no way to load a local file into a service using the CLI alone. tiger db query can run SQL from a file, but it can't stream data, so COPY ... FROM STDIN isn't reachable and neither is anything equivalent. Today the only options are to install psql for \copy or to write a program against a Postgres driver.
Why this matters
The CLI's own description is "an MCP server for helping coding agents write production-level Postgres code." Getting data into a service is step one of most of that work, and it's currently the one step that requires stepping outside the CLI.
Concretely: I built a workshop where attendees point a coding agent at a Tiger Cloud service in a GitHub Codespace. Every other operation — DDL, queries, EXPLAIN, schema inspection, forking, teardown — is tiger and nothing else. Loading 1M rows of NYC 311 data is the sole reason a second tool has to exist in the image.
What I tried
1. COPY ... FROM STDIN — hangs indefinitely. This is arguably a bug on its own:
$ tiger db query <service-id> -c "COPY t FROM STDIN WITH (FORMAT csv, HEADER true)" < data.csv
# never returns; had to kill it
The server is waiting for CopyData frames the client never sends, and the client is waiting for a result set that will never arrive. An immediate "COPY FROM STDIN is not supported" would be much kinder than a hang.
2. -f with a file of INSERTs — works, but doesn't scale. tiger db query sends a file as one query string, so a 146 MB file of 1M rows gets:
Error: ERROR: out of memory (SQLSTATE 53200)
against a free service.
3. The workaround I shipped. Chunk the data into 10 gzipped files of 100k INSERTs each (~15 MB apiece) and stream them through stdin:
for f in data/load/*.sql.gz; do
gunzip -c "$f" | tiger db query "$SERVICE_ID"
done
This works and needs nothing but tiger and gunzip. It takes 78 seconds for 1M rows against a free service. The same data via a real COPY ... FROM STDIN (Node, pg-copy-streams) takes 12–17 seconds, so the workaround is roughly 5x slower and ships the dataset in a format that's ~6x larger uncompressed than the CSV it came from.
What would help
Something like:
tiger db copy <service-id> --table service_requests --from data.csv --format csv --header
tiger db copy <service-id> --table service_requests --from - # stdin
Reading from stdin matters as much as the file case — it keeps gunzip -c … | and other pipelines working without unpacking large files to disk.
Two smaller things that would each be worth having on their own, even without the above:
- Make
COPY ... FROM STDINviatiger db queryfail fast with a clear message instead of hanging. - Consider streaming
-ffiles statement-by-statement rather than as one query string, so a large SQL file doesn't OOM the server.
Environment
Tiger CLI 0.24.0, darwin/arm64 and linux/arm64 (devcontainer). Reproduced against both a free (shared/shared) service and a paid 0.5 CPU / 2 GB service in us-east-1.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the tiger db query command and reproduce the hanging COPY ... FROM STDIN case using the issue's example. Define the CLI entry point and data-streaming behavior for tiger db copy, including file and stdin input, then verify that large CSV loads complete without requiring psql or a driver. Also confirm unsupported COPY FROM STDIN queries fail promptly with a clear message.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go, postgresql
- Domain
- cli, databases
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100