Skip to content

tiger db: no way to load a local file (no COPY ... FROM STDIN equivalent) #227

Description

@mattstratton

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 STDIN via tiger db query fail fast with a clear message instead of hanging.
  • Consider streaming -f files 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions