trdsql: SQL against CSV, JSON, YAML, LTSV and other flat files
CLI tool that can execute SQL queries on CSV, LTSV, JSON, YAML and TBLN. Can output to various formats.
At a glance
- What is it?
- trdsql loads structured text files into a real database engine and runs ordinary SQL on them, which makes it a middle path between grep, jq and loading a file into PostgreSQL yourself. The interesting parts are the format guessing, the driver choice and a build story the README and the Dockerfile do not agree on.
- Who is it for?
- trdsql earns its place for the cases where a file is too structured for grep and too big to load into a database by hand, and it earns that place without asking you to learn a query language. What to check before committing to it is narrower than the feature list suggests.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 43 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 11, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A query engine pointed at files instead of tables
trdsql is a Go command line tool that executes SQL queries against CSV, LTSV, JSON, YAML and TBLN files. The README frames it against two well known predecessors, q and textql, and names the difference in one sentence: trdsql lets you write PostgreSQL or MySQL syntax. That distinction sounds small until you try to remember which dialect a given tool expects, because most text-as-query tools grow their own vocabulary for fields, filters and grouping.
The design underneath is straightforward once you know it. A file becomes a table, and the table lives in a real database engine, so the query is parsed and executed by SQLite, PostgreSQL or MySQL rather than by the tool itself. That is why the driver flag exists and why the default matters:
trdsql [options] SQLIt also means the output formats do not have to be invented. If the engine can return rows, the tool can print them as CSV, JSON, YAML, LTSV, a Markdown table or something else, and the same row set can go to a file or to standard output.
There is a second mode that is easy to miss. The README describes plain file conversion as equivalent to running trdsql with SELECT * against the file, which means you can convert a file without writing a query at all:
trdsql -o[output format] -t [input filename]That single line is the difference between a query tool and a format converter, and it is the part most people end up using.
Format guessing and the options that shape a table
The input flag set is the part of trdsql that shows how much thought went into real files rather than ideal ones. Format detection is the default, marked -ig, meaning the reader guesses from the file extension. Anything you would rather state explicitly has its own flag: -icsv, -ijson, -iltsv, -iyaml, -itbln, plus three less common ones, -iwidth for fixed width text, -itext for plain text, and -ilogfmt for logfmt lines.
Then there are the options that decide what a column is. -ih treats the first line as column names for CSV. -id sets the field delimiter, defaulting to a comma. -is skips header rows, which is the option you need for the CSV files that begin with a comment or a report title. -ir sets how many rows are read ahead, defaulting to one, and -ilr caps the total number of rows read. -inum adds a row number column, which is a practical way to keep a pointer back to the original file. -inull gives you a value to convert to null on input, and -ijq takes a jq expression for JSON and JSONL input.
A few of those options are explicitly scoped in the README, and the scoping is worth reading carefully. Header handling and delimiter changes are marked as CSV only, while -ijq applies to JSON and JSONL only. If you reach for -ijq on a CSV file expecting it to filter columns, it will not do what you think.
Letting the tool write the query for you
The flags that surprise people are the analyze pair. -a with a filename analyzes the file and suggests SQL, and -A analyzes the file but prints only the SQL and nothing else. On a file whose column layout you do not know, that is a fast way to get a starting query instead of guessing header names by eye.
Query and table names can also come from files rather than the command line. -q reads a query from a named file, and -t reads the table name from a named file. Both matter for scripts, where a long query with shell quoting in it is a persistent source of breakage. The configuration side is handled by -config for a configuration file location, -db for the named setting, and -dblist to print the list of configured databases. A sample file named config.json.sample ships in the repository root, so the shape of a connection setting can be read directly rather than guessed.
The driver flag is the other half of the configuration story. -driver accepts mysql, postgres, sqlite3, or sqlite for the CGO-free build, with sqlite3 as the default, and -dsn supplies the driver specific data source name. That default matters more than it first appears, because sqlite3 is not the free-standing choice it sounds like. The default build path pulls in a cgo dependency, and the README says so in its own section on cgo-free builds.
The cgo-free story does not match the dependency list
The README has a section on building without cgo. It says that go-sqlite3 is typically used for building, and that if you build with CGO_ENABLED=0 you should consider using sqlite instead, linking to a repository at gitlab.com/cznic/sqlite. It also notes the honest cost: building without cgo can reduce cross-compilation problems but may run slower.
The go.mod in the repository does not list that dependency. What it lists is github.com/mattn/go-sqlite3 v1.14.50, the cgo driver named as the default, alongside modernc.org/sqlite v1.57.0, the pure Go translation that carries a large set of indirect dependencies beneath it, including modernc.org/libc, modernc.org/mathutil and modernc.org/memory. cznic/sqlite does not appear.
Both statements can be true at once, of course. A README written years ago would have named the driver that was current then, and go.mod records what the code needs now. The reader still has to decide which description to trust, and the repository makes that check easy. The source tree holds both database_connect.go and database_connect_cgo.go, so the cgo-free path is a separate file with its own build handling rather than a runtime option. The Makefile adds a third piece of information: it builds with the tag sqlite_math_functions, which is what gives the sqlite driver the extra math functions.
The practical consequence is that the cgo-free route is not the one the README names, and the answer to which driver a no-cgo build actually loads is in the build-tagged source file, not in the prose.
Go 1.25 in the docs, Go 1.18 in the Dockerfile
"The install section of the README states its requirement plainly: go 1.25 or higher. The go.mod agrees, opening with a go directive of 1.25.0. Building from source is the documented route for anyone with Go installed:
go get -d github.com/noborus/trdsql
cd $GOPATH/src/github.com/noborus/trdsql
make
make installThe Dockerfile disagrees. Its first line is FROM golang:1.18-alpine as build-dev, and it clones the repository inside that image, runs make and make install, then copies the resulting binary into a final stage built FROM alpine:latest:
FROM alpine:latest
COPY --from=build-dev /usr/local/bin/trdsql /usr/local/bin/trdsql
ENTRYPOINT ["trdsql"]A Go 1.18 toolchain encountering a module that declares go 1.25.0 will not simply build it. Modern Go refuses to proceed rather than attempting a build it knows will be wrong, so the image build is the exact place this mismatch becomes visible. That is also why it is worth checking rather than assuming, since the repository offers a prebuilt image as the easy path:
docker pull noborus/trdsqlThe README gives no warning about the base image version, and the Dockerfile carries no comment about it either. Anyone who wants to know whether the published image is current has to compare three files, or build it and read the error.",
Recent releases and the shape of the codebase
Three releases are recent enough to describe the maintenance picture. v1.2.3, published 2026-05-29, fixed XGO installation in the release workflow and updated the Go version. v1.2.2, published the same day, rebuilt the v1.2.1 binary because the Windows build was corrupted, tracked as issue 335. v1.2.1, published 2026-05-12, fixed the release build environment. The last push to the repository was on 2026-08-29, which is after all three tags.
That sequence says something specific. Two of the last three releases were about getting the build and the packaging working rather than about changing what the tool does, and one was a repair of a broken artifact. Release mechanics are a real part of a distributed command line tool, since the Homebrew, MacPorts and FreshBSD packages all depend on the release assets existing and being sound, but it is worth knowing what the recent tags represent before reading a version number as a feature marker.
The file layout explains the rest. Input and output are symmetric, with input_csv.go, input_json.go, input_ltsv.go, input_logfmt.go, input_tbln.go, input_text.go and input_yaml.go, matched by output_csv.go, output_json.go, output_jsonl.go, output_ltsv.go and output_raw.go. The importer has both a buffered and a sliced implementation, importer_buffer.go and importer_slice.go, which is what lets the same tool run a streaming query over a large file or hold rows in memory for a small one. There is also analyze.go, a config sample, a completion directory for shell completion, and an _example directory that exists for the library audience the README points at on godoc.
Editorial conclusion
trdsql earns its place for the cases where a file is too structured for grep and too big to load into a database by hand, and it earns that place without asking you to learn a query language. What to check before committing to it is narrower than the feature list suggests. Pick the driver deliberately, since the default sqlite3 driver pulls in cgo while the CGO-free path is a different dependency set. Read the Dockerfile before you trust the image, because it declares a Go base image far older than the version the README and go.mod ask for. And decide early whether you want a query or a conversion, because the plain conversion form takes no SQL at all and quietly takes a different code path through the importer.
Frequently asked questions
What does trdsql actually do with a CSV file?
It loads the file as a table into a database engine and runs SQL against it, which means the query is parsed by SQLite, PostgreSQL or MySQL rather than by trdsql itself. The default engine is SQLite through the sqlite3 driver, and the file can also be converted without any query by using the output format and table name flags together.
How do I build trdsql without cgo?
The README points at a sqlite driver hosted on GitLab for CGO-free builds and warns that it can run slower. The go.mod instead lists modernc.org/sqlite alongside the cgo driver go-sqlite3, and the repository has a separate database_connect_cgo.go, so the cgo-free path lives in its own source file. Check which driver that file registers before assuming the README is current.
Why does the trdsql Docker image fail to build from source?
The Dockerfile starts from golang:1.18-alpine, while go.mod declares go 1.25.0 and the README asks for Go 1.25 or higher. A toolchain that old will refuse the newer module directive. The published image can be pulled with docker pull noborus/trdsql, and the run form mounts the current directory at /tmp.
Which file formats can trdsql read and write?
On input: CSV, LTSV, JSON, YAML, TBLN, fixed width text, plain text and logfmt, with the format guessed from the extension unless you pass an explicit flag such as -icsv or -ijson. On output: CSV, JSON, JSONL, LTSV, YAML and raw, along with table and Markdown rendering. The jq expression option applies to JSON and JSONL input only.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/noborus-trdsql)