https://github.com/sourcegraph/querygen
CLI + tiny library for more readable SQL queries
https://github.com/sourcegraph/querygen
golang sql
Last synced: 21 days ago
JSON representation
CLI + tiny library for more readable SQL queries
- Host: GitHub
- URL: https://github.com/sourcegraph/querygen
- Owner: sourcegraph
- License: apache-2.0
- Created: 2024-06-08T13:23:21.000Z (almost 2 years ago)
- Default Branch: main
- Last Pushed: 2024-06-09T12:35:50.000Z (almost 2 years ago)
- Last Synced: 2026-03-08T19:55:35.191Z (28 days ago)
- Topics: golang, sql
- Language: Go
- Homepage:
- Size: 54.7 KB
- Stars: 3
- Watchers: 2
- Forks: 1
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# querygen: CLI + tiny library for readable SQL queries
`querygen` enables writing [parameterized SQL queries](https://en.wikipedia.org/wiki/Prepared_statement)
using string interpolation-like syntax. Since Go lacks
features like macros, quasiquotes or native string interpolation,
we resort to ~heinous crimes~ codegen.
## Usage
Write a SQL query in your Go code using interpolation-like syntax:
```go
package cakes // cakes.go
const partyAttendeesQuery = `
SELECT person_name
FROM party_attendees
WHERE party = {{partyId : int}}
`
const bestChoiceCakeQuery = `
WITH attendees AS (` + partyAttendeesQuery + `)
SELECT fave_cakes.cake_type
FROM attendees JOIN fave_cakes ON attendees.person_name = fave_cakes.person_name
-- Need to allow host to exclude one cake they don't like
WHERE fave_cake.cake_type != {{excludedCakeType : string}}
GROUP BY fave_cake.cake_type
ORDER BY COUNT(fave_cake.cake_type) DESC
LIMIT 1
`
```
Get the [`querygen` CLI](https://github.com/sourcegraph/querygen/releases)
and run it on the package.
```bash
go install github.com/sourcegraph/querygen/cmd/querygen@latest
querygen ./...
```
This will generate a file next to the original file:
```go
// Code generated by querygen.
// You may only edit import statements.
package cakes // cakes_query_gen.go
import (
"github.com/sourcegraph/querygen/lib/interpolate"
)
type partyAttendeesQueryVars struct {
partyId int
}
var _ interpolate.QueryVars = &partyAttendeesQueryVars{}
// methods omitted...
type bestChoiceCakeQueryVars struct {
partyId int
excludedCakeType string
}
var _ interpolate.QueryVars = &bestChoiceCakeQueryVars{}
// methods omitted...
```
You can use these structs with `interpolate.Do(myQuery, &myQueryVars{...})` function
to generate a [`*sqlf.Query`](https://sourcegraph.com/search?q=context:global+repo:%5Egithub%5C.com/keegancsmith/sqlf%24%40master+file:sqlf.go+type:symbol+Query&patternType=keyword&sm=0)
which can then be executed. The `interpolate.Do` function replaces the `sqlf.Sprintf`
function.
For more complex usage, see [Reference.md](docs/Reference.md).
## Motivation and Comparison
The Sourcegraph monorepo largely uses the [`sqlf`](github.com/keegancsmith/sqlf) library
for constructing SQL queries. `querygen` sits on top of `sqlf` to enable:
1. Incrementally improving the readability of existing queries
2. Add compile-time checking for names and number of arguments
without having to switch to an alternate SQL driver.
| Tool/Library | `sqlf` | `querygen` | `pgx` | `sqlc` |
|---------------------------------------|:------------:|:-------------:|:--------:|:-------------:|
| Operation | Run-time | ~Compile-time | Run-time | ~Compile-time |
| Named parameters in query syntax | ❌ | ✅ | ✅ | ✅ |
| Static parameter name checking | ❌ | ✅ | ❌ | ✅ |
| Static binding var count checking (†) | ❌ | ✅ | ❌ | ✅ |
| Arbitrary dynamic query fragments | ✅ | ✅ | ✅ | ❌ |
| Static query validation | ❌ | ❌ | ❌ | ✅ |
| Statically checked row scanning | ❌ | ❌ | ❌ | ✅ |
(†) Caveat: Static binding var count checking requires using
the [exhaustruct](https://golangci-lint.run/usage/linters/#exhaustruct)
linter or similar for the types generated for query variables.
We could probably use `sqlc` for common cases,
but the Sourcegraph codebase has some [complex dynamically assembled queries](https://sourcegraph.com/github.com/sourcegraph/sourcegraph@d288874197bed9c219c20a01cd7786e1d2aa6e11/-/blob/internal/batches/store/batch_changes.go?L612-697)
which cannot be statically analyzed by `sqlc`.
So those would need to be either re-written to fit into
`sqlc`'s model, or you'd need some sophisticated
[abstract interpretation](https://en.wikipedia.org/wiki/Abstract_interpretation)
to extract all possible queries and analyze them individually.
## Contributing
See [Development.md](docs/Development.md) for build instructions etc.
At the moment, this is made primarily for Sourcegraph's internal use.
Usage outside Sourcegraph is not supported.