From 5fb350c8f7443b585a1847c89f4e30235097a9b0 Mon Sep 17 00:00:00 2001 From: Wael Nasreddine Date: Fri, 20 Mar 2026 22:41:56 -0700 Subject: [PATCH 1/2] feat: make engines configurable via --engine CLI flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, Run() hardcoded three engines (sqlite, postgres, mysql), which forced all callers to generate wrappers for all three databases. This became a problem for stowix/uar which dropped MySQL support — sqlc-multi-db would still try to open mysqldb/ directories that no longer exist. This change: - Adds a repeatable --engine name:package flag to the CLI so callers opt in to exactly the engines they need. - Removes the hardcoded engines slice from generator.Run(), passing it as a parameter instead. - Exits with a fatal error when no --engine flags are provided. - Updates README.md to document the new --engine flag and updated library API. --- README.md | 50 ++++++++++++++++++++++++++++--------- generator/generator.go | 8 +----- main.go | 56 +++++++++++++++++++++++++++++++++--------- 3 files changed, 84 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 7c2a769..bb5eddd 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,7 @@ When using sqlc with multiple database backends, each engine generates its own ` - `generated_querier.go` — a common `Querier` interface in the parent package - `generated_models.go` — common domain model types (converted from engine-specific types) - `generated_errors.go` — shared sentinel errors (`ErrNotFound`, `ErrMismatchedSlices`) -- `generated_wrapper_sqlite.go` — SQLite wrapper implementing the common `Querier` -- `generated_wrapper_postgres.go` — PostgreSQL wrapper implementing the common `Querier` -- `generated_wrapper_mysql.go` — MySQL/MariaDB wrapper implementing the common `Querier` +- `generated_wrapper_.go` — one wrapper per engine, implementing the common `Querier` The wrappers handle engine differences automatically: @@ -23,8 +21,8 @@ The wrappers handle engine differences automatically: ## Requirements - Go 1.24+ (uses the `tool` directive in `go.mod`) -- sqlc with queries for all three engines: `query.sqlite.sql`, `query.postgres.sql`, `query.mysql.sql` -- sqlc output packages named `sqlitedb`, `postgresdb`, `mysqldb` (siblings of the target package) +- sqlc with queries for the engines you need (e.g. `query.sqlite.sql`, `query.postgres.sql`) +- sqlc output packages named to match the `--engine` flags you pass (siblings of the target package) ## Installation @@ -44,12 +42,39 @@ require github.com/kalbasit/sqlc-multi-db vX.Y.Z ## Usage +``` +sqlc-multi-db --engine name:package [--engine ...] /path/to/source/querier.go +``` + +The `--engine` flag is **repeatable** and takes the form `name:package`: + +- `name` — engine identifier used in generated file names (e.g. `sqlite`, `postgres`, `mysql`) +- `package` — directory name of the sqlc-generated package for that engine (e.g. `sqlitedb`, `postgresdb`) + +At least one `--engine` flag is required; the tool exits with an error if none are provided. + +### Examples + +SQLite + PostgreSQL only: + +```bash +go tool sqlc-multi-db --engine sqlite:sqlitedb --engine postgres:postgresdb postgresdb/querier.go +``` + +All three engines: + +```bash +go tool sqlc-multi-db --engine sqlite:sqlitedb --engine postgres:postgresdb --engine mysql:mysqldb postgresdb/querier.go +``` + +### go:generate + Add a `generate.go` file in your database package (e.g., `pkg/database/generate.go`): ```go package database -//go:generate go tool github.com/kalbasit/sqlc-multi-db postgresdb/querier.go +//go:generate go tool sqlc-multi-db --engine sqlite:sqlitedb --engine postgres:postgresdb postgresdb/querier.go ``` Then run: @@ -64,16 +89,14 @@ go generate ./pkg/database pkg/database/ sqlitedb/ # sqlc-generated (sqlite engine) postgresdb/ # sqlc-generated (postgres engine) ← source of truth - mysqldb/ # sqlc-generated (mysql engine) database.go # your Open() factory errors.go # your custom errors (IsDeadlockError, etc.) generate.go # //go:generate directive - generated_errors.go # generated - generated_models.go # generated - generated_querier.go # generated + generated_errors.go # generated + generated_models.go # generated + generated_querier.go # generated generated_wrapper_sqlite.go # generated generated_wrapper_postgres.go # generated - generated_wrapper_mysql.go # generated ``` ## Bulk Operations (`@bulk-for`) @@ -122,7 +145,10 @@ The generator logic is also available as a library: ```go import "github.com/kalbasit/sqlc-multi-db/generator" -generator.Run("/path/to/postgresdb/querier.go") +generator.Run("/path/to/postgresdb/querier.go", []generator.Engine{ + {Name: "sqlite", Package: "sqlitedb"}, + {Name: "postgres", Package: "postgresdb"}, +}) ``` ## License diff --git a/generator/generator.go b/generator/generator.go index 5ff4fed..2fc3921 100644 --- a/generator/generator.go +++ b/generator/generator.go @@ -18,13 +18,7 @@ import ( // Run is the main entry point for the generator. // querierPath is the path to the source querier.go file (e.g., postgresdb/querier.go). -func Run(querierPath string) { - engines := []Engine{ - {Name: "sqlite", Package: "sqlitedb"}, - {Name: "postgres", Package: "postgresdb"}, - {Name: "mysql", Package: "mysqldb"}, - } - +func Run(querierPath string, engines []Engine) { absQuerierPath, err := filepath.Abs(querierPath) if err != nil { log.Fatalf("resolving querier path: %v", err) diff --git a/main.go b/main.go index 89180c5..71d044e 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,9 @@ package main import ( + "errors" + "flag" + "fmt" "log" "os" "strings" @@ -8,24 +11,55 @@ import ( "github.com/kalbasit/sqlc-multi-db/generator" ) +var errInvalidEngineFormat = errors.New("invalid engine format: expected name:package") + +type engineFlag []generator.Engine + +func (e *engineFlag) String() string { + parts := make([]string, len(*e)) + for i, eng := range *e { + parts[i] = eng.Name + ":" + eng.Package + } + + return strings.Join(parts, ", ") +} + +func (e *engineFlag) Set(value string) error { + parts := strings.SplitN(value, ":", 2) + if len(parts) != 2 || parts[0] == "" || parts[1] == "" { + return fmt.Errorf("%w: got %q", errInvalidEngineFormat, value) + } + + *e = append(*e, generator.Engine{Name: parts[0], Package: parts[1]}) + + return nil +} + func main() { - var querierPath string - // Handle cases where go run might pass "--" - for _, arg := range os.Args[1:] { - if arg != "--" && !strings.HasPrefix(arg, "-") { - querierPath = arg - - break - } + var engines engineFlag + + flag.Var(&engines, "engine", "Engine in name:package format (repeatable)") + + flag.Usage = func() { + fmt.Fprintf(os.Stderr, "USAGE: %s [--engine name:package ...] /path/to/source/querier.go\n", os.Args[0]) } - if querierPath == "" { - log.Fatalf("USAGE: %s /path/to/source/querier.go", os.Args[0]) + flag.Parse() + + if len(engines) == 0 { + log.Fatalf("USAGE: sqlc-multi-db --engine name:package [--engine ...] /path/to/querier.go") + } + + args := flag.Args() + if len(args) == 0 { + log.Fatalf("USAGE: sqlc-multi-db --engine name:package [--engine ...] /path/to/querier.go") } + querierPath := args[0] + if _, err := os.Stat(querierPath); err != nil { log.Fatalf("stat(%q): %s", querierPath, err) } - generator.Run(querierPath) + generator.Run(querierPath, []generator.Engine(engines)) } From 32be278697f93894f0df7d59698d7c6cddc53f66 Mon Sep 17 00:00:00 2001 From: Wael Nasreddine Date: Fri, 20 Mar 2026 22:54:37 -0700 Subject: [PATCH 2/2] fix: address PR comments on argument parsing and README examples - Use flag.NArg() and flag.Arg() for cleaner argument validation - Call flag.Usage() on error instead of duplicating the message - Use full package path in go tool examples for consistency Co-Authored-By: Claude Opus 4.6 --- README.md | 6 +++--- main.go | 12 ++++-------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index bb5eddd..fb2bea0 100644 --- a/README.md +++ b/README.md @@ -58,13 +58,13 @@ At least one `--engine` flag is required; the tool exits with an error if none a SQLite + PostgreSQL only: ```bash -go tool sqlc-multi-db --engine sqlite:sqlitedb --engine postgres:postgresdb postgresdb/querier.go +go tool github.com/kalbasit/sqlc-multi-db --engine sqlite:sqlitedb --engine postgres:postgresdb postgresdb/querier.go ``` All three engines: ```bash -go tool sqlc-multi-db --engine sqlite:sqlitedb --engine postgres:postgresdb --engine mysql:mysqldb postgresdb/querier.go +go tool github.com/kalbasit/sqlc-multi-db --engine sqlite:sqlitedb --engine postgres:postgresdb --engine mysql:mysqldb postgresdb/querier.go ``` ### go:generate @@ -74,7 +74,7 @@ Add a `generate.go` file in your database package (e.g., `pkg/database/generate. ```go package database -//go:generate go tool sqlc-multi-db --engine sqlite:sqlitedb --engine postgres:postgresdb postgresdb/querier.go +//go:generate go tool github.com/kalbasit/sqlc-multi-db --engine sqlite:sqlitedb --engine postgres:postgresdb postgresdb/querier.go ``` Then run: diff --git a/main.go b/main.go index 71d044e..326e95d 100644 --- a/main.go +++ b/main.go @@ -46,16 +46,12 @@ func main() { flag.Parse() - if len(engines) == 0 { - log.Fatalf("USAGE: sqlc-multi-db --engine name:package [--engine ...] /path/to/querier.go") + if len(engines) == 0 || flag.NArg() != 1 { + flag.Usage() + os.Exit(1) } - args := flag.Args() - if len(args) == 0 { - log.Fatalf("USAGE: sqlc-multi-db --engine name:package [--engine ...] /path/to/querier.go") - } - - querierPath := args[0] + querierPath := flag.Arg(0) if _, err := os.Stat(querierPath); err != nil { log.Fatalf("stat(%q): %s", querierPath, err)